I am attempting to extend the communication distance between Peripheral and Central using codedPhy with XIAO_nRF54L15. In previous experiments, it appears the Central can receive data advertised by Peripheral using codedPhy without establishing a connection. However, when attempting to connect, the error bt_hci_core: opcode 0x2041 status 0x11 occurs, preventing connection.
If anyone has successfully connected using codedPhy, please share your information.
Hi there,
So I went and built both the Central HR coded and Peripheral HR coded Examples for both DK’s I have nRf54l15DK as Central & the Peripheral on the nRF52840DK
Indeed it does connect automatically just as a sanity check. I will build and try it for the Xiao nRF54L15 as Both and report back.
*** Booting nRF Connect SDK v3.1.1-e2a97fe2578a ***
*** Using Zephyr OS v4.1.99-ff8f0c579eeb ***
Starting Bluetooth Central HR coded sample
I: SoftDevice Controller build revision:
I: fc de 41 eb a2 d1 42 24 |..A...B$
I: 00 b5 f8 57 9f ac 9d 9e |...W....
I: aa c9 b4 34 |...4
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF54Lx (0x0005)
I: Firmware: Standard Bluetooth controller (0x00) Version 252.16862 Build 1121034987
I: HCI transport: SDC
I: Identity: F0:3A:D1:2E:FE:8F (random)
I: HCI: version 6.1 (0x0f) revision 0x3069, manufacturer 0x0059
I: LMP: version 6.1 (0x0f) subver 0x3069
Bluetooth initialized
Scanning successfully started
When I switch ON the Peripheral power switch it connects straight away.
---- Opened the serial port COM12 ----
Filters matched. Address: C9:40:B4:64:BA:4D (random) connectable: yes Primary PHY: S=8 Coded Secondary PHY S=8 Coded
Connection pending
Connected: C9:40:B4:64:BA:4D (random), tx_phy LE Coded, rx_phy LE Coded
The discovery procedure succeeded
[SUBSCRIBED]
Heart Rate Measurement notification received:
Heart Rate Measurement Value Format: 8 - bit
Sensor Contact detected: 1
Sensor Contact supported: 1
Energy ExpendedHeart Rate Measurement notification received:
Heart Rate Measurement Value Format: 8 - bit
Sensor Contact detected: 1
Sensor Contact supported: 1
Energy Expended present: 0
RR-Intervals present: 0
Heart Rate Measurement notification received:
Heart Rate Measurement Value Format: 8 - bit
Sensor Contact detected: 1
Sensor Contact supported: 1
Energy Expended present: 0
HTH
GL
PJ ![]()
I ran some Ai on it I get this:
on the XIAO board, there are a few ways things can go wrong:
- Controller not actually built with coded PHY support
- If
CONFIG_BT_CTLR_PHY_CODED=yis missing for the controller, but the host app still tries to use coded PHY (viaprj.conf), you get exactly this: “Unsupported Feature or Parameter Value” from the HCI command.
- Host requesting a PHY combo that controller doesn’t like
- For example, initiating a connection with only coded PHY set, wrong PHY bitmask, or odd interval/window values can cause the controller to reject the
LE Extended Create Connectionor scanning setup and log0x2041 / 0x11.
- XIAO board definition slightly out of sync with the Nordic DK config
- Seeed’s XIAO board files are layered on top of NCS; it’s easy for a single missing Kconfig (like PHY_CODED) or SoftDevice option to slip through.
Given they can already receive coded PHY advertising, I’d bet:
The central is built with
CONFIG_BT_PHY_UPDATE/ coded support on the host side, but the controller (SoftDevice) for the XIAO build is missingCONFIG_BT_CTLR_PHY_CODEDor a matching option.
So scanning “sort of works”, but when Zephyr tries to enable a specific extended scan/initiator combo for connection, SDC refuses it.
I will study it further tomorrow and report back. ![]()
Hi PJ,
Thank you for always providing information.
I’ve been working on evaluating CodedPhy for quite some time now, but it’s become too much for me to handle.
If you find out anything, please let me know.
Hi there,
So Great News, I was able to look with fresh eyes and get it going after Lunch
Turns out the dam Uart20 thing again,
- I switched from UART to SEGGER RTT for bring-up.
- Required because the XIAO DTS assigns
uart20aszephyr,console, which caused a UARTE ISR assert on this board.
I used an Overlay do disable the consoles in the code.
&uart20 { status = "disabled"; };
&uart21 { status = "disabled"; };
- Peripheral advertises using extended advertising on LE Coded PHY.
- Central (nRF54L15 DK) connects immediately.
- Verified coded connection:
tx_phy LE Coded, rx_phy LE Coded
this is my RTT output ![]()
00> *** Booting nRF Connect SDK v3.1.1-e2a97fe2578a ***
00> *** Using Zephyr OS v4.1.99-ff8f0c579eeb ***
00> Starting Bluetooth Peripheral HR coded sample
00> I: SoftDevice Controller build revision:
00> I: fc de 41 eb a2 d1 42 24 |..A...B$
00> I: 00 b5 f8 57 9f ac 9d 9e |...W....
00> I: aa c9 b4 34 |...4
00> I: HW Platform: Nordic Semiconductor (0x0002)
00> I: HW Variant: nRF54Lx (0x0005)
00> I: Firmware: Standard Bluetooth controller (0x00) Version 252.16862 Build 1121034987
00> I: HCI transport: SDC
00> I: Identity: D9:34:FF:FF:FE:0C (random)
00> I: HCI: version 6.1 (0x0f) revision 0x3069, manufacturer 0x0059
00> I: LMP: version 6.1 (0x0f) subver 0x3069
00> Bluetooth initialized
00> Created adv: 0x200023c8
00> Advertiser 0x200023c8 set started
00> Connected: F0:3A:D1:2E:FE:8F (random), tx_phy LE Coded, rx_phy LE Coded
00> I: HRS notifications enabled
00> I: HRS notifications disabled
00> Disconnected, reason 0x08
00> Advertiser 0x200023c8 set started
00> Connected: F0:3A:D1:2E:FE:8F (random), tx_phy LE Coded, rx_phy LE Coded
00> I: HRS notifications enable
No BLE changes, just move stuff that gets in the way. ![]()
attached is the peripheral Hex file.
merged.zip (200.9 KB)
I created a “Xiao_disable_uart.overlay” in the app root, build and BOOM!
/* Disable the console UART that is crashing */
&uart20 {
status = "disabled";
};
/* Optional: also disable uart21 if anything still tries to use it */
&uart21 {
status = "disabled";
};
and the RTT setup for the build,
I used this from the other builds, because it just works.
“prj_rtt.conf”
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=n
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_RTT_CONSOLE=y
# Keep UART completely out of the picture
CONFIG_UART_CONSOLE=n
CONFIG_LOG_BACKEND_UART=n
# Also keep shell off during bring-up
CONFIG_SHELL=n
M|y setup is nRF54L15 DK (Nordic’s \Central_hr_coded example) AS-IS built for DK
XIAO nRF54L15 connected to DK , using J19 (for external JLink/SWD)
Once the hardware connection is made I don’t touch the DUT only Jlink commands:
- Connect
- Halt
- Erase
- Loadfile D :\Nordic\myapps\workspace\peripheral_hr_coded\build_xiao_periph\merged.hex
- RESET
- GO (these last two are interchangable)
after which I get the RTT message above , and then the console on the central start displaying HR , ![]()
Message report Coded PHY connection is successfully made.
Welcome to Level 2

HTH
GL
PJ ![]()
if you press the reset on the Xiao Central shows this ;
Disconnected: D9:34:FF:FF:FE:0C (random), reason 0x08
Filters matched. Address: D9:34:FF:FF:FE:0C (random) connectable: yes Primary PHY: S=8 Coded Secondary PHY S=8 Coded
Connection pending
Connected: D9:34:FF:FF:FE:0C (random), tx_phy LE Coded, rx_phy LE Coded
The discovery procedure succeeded
[SUBSCRIBED]
Heart Rate Measurement notification received:
Heart Rate Measurement Value Format: 8 - bit
Sensor Contact detected: 1
Sensor Contact supported: 1
Energy Expended present: 0
RR-Intervals present: 0
Re-connects so fast it’s GREAT! "![]()
ps. I did edit the peripheral example for the Xiao Led’s
//#define RUN_STATUS_LED DK_LED1
//#define CON_STATUS_LED DK_LED2
#define RUN_STATUS_LED DK_LED1
#define CON_STATUS_LED DK_LED1 /* XIAO has only one usable LED */
Season’s Greetings ![]()
Thank you for the information. I’m relieved to confirm that it’s possible.
So peripheral_hr_coded and central_hr_coded work on XIAO_nRF54L15, correct?
Should I build it as nRF54L15DK and upload the hex file to XIAO_nRF54L15?
To run this sample on the XIAO_nRF54L15, do I need to disable uart20 and uart21 and use SEGGER RTT? Or is it possible it might work even when using XIAO’s serial monitor?
Thanks to your hints, I managed to get both peripheral_hr_coded and central_hr_coded running on XIAO_nRF54L using the serial monitor after some trial and error.
Hi there,
Awesome, I also was able to use just the console. I like the RTT though Nothing to connect or disconnect
I got a range tester working and testing ![]()
- UART console can work if the board’s “chosen console” UART actually maps to the USB-serial path you’re using and doesn’t conflict with other peripherals — but on this platform, that’s been the source of crashes and “???”, and you already hit a UARTE assert.
So: No, you don’t always have to disable UART, but yes, for this sample and for sanity, RTT is the recommended method.
Because the XIAO board’s DTS had console/shell bound to a UART instance (uart20 in your zephyr.dts “chosen” section). When that UART isn’t physically connected the way the firmware expects (or pinctrl / clocks / ownership are wrong), the driver can hit asserts during IRQ handling — which is exactly what you saw.
Best practice I use
- Build the sample using:
-b xiao_nrf54l15/nrf54l15/cpuapp-DBOARD_ROOT=...platform-seeedboards/zephyr
- Use SEGGER RTT for console while validating BLE + coded PHY.
- Only after it’s stable, revisit UART console if it really needs it.
I love the speed at which it connects with the coded PHY it is very fast.![]()
HTH
GL
PJ ![]()
The cause of the “ bt_hci_core: opcode 0x2041 status 0x11” error in my code is still unclear, but since codedPhy has been confirmed to work, I will proceed with debugging by comparing it with central_hr_coded.
HI there,
So I would agree
I’m just glad it works, I did more investigation on the Error you were getting, and I found these;
- Opcode
0x2041= HCI LE Set Extended Scan Enable command (OGF 0x08, OCF 0x0041) in the Bluetooth spec. Zephyr logs the same opcode when extended scanning trips up. GitHub - Status
0x11= “Unsupported Feature or Parameter Value” in HCI status codes. Nordic DevZone
So the controller (SoftDevice Controller on nRF54L15) is basically saying:
“The way you’re trying to scan/initiate (PHY/params) is not supported.”
That’s why you can see coded PHY advertising (some of the config is working), but when they try to take it further into a connection, a specific HCI command with those settings gets rejected. I’m fighting the RSSI info for now, not supported are some of the old ways to obtain it. ![]()
As I understand it the controller needs specific pjr.conf setup to do "coded PHY " properly.
Thank you for asking the question, It pushed me to do more and look closer, now it’s working here very well and FAST
I’ll break out some antennas and Test at the Park (open air) I have Central and Peripheral built for the Xiao Dev Expansion board to get untethered results but I’m already certain it will go further than the nRF52840 I have on m|y PCB
![]()
instead of forcing initial coded PHY first. I’m using PHY “Update”
A safer pattern (and one Nordic actually uses in some examples in Dev academy):
- Advertise with coded PHY (extended adv).
- Connect using default 1M PHY.
- Once connected, call PHY update to switch to coded:
for me this is the Solution and,
I have arrived at the conclusion though to keep the USB-C for flashing and the RTT for logging and debug. staying away from the UARTE and
Onward and Upward… ![]()
HTH
GL
PJ ![]()
I have already tested connecting via 1M-phy and then communicating using coded-phy with XIAO_nRF52840. However, since it’s 1M-phy, proximity is required for connection. With XIAO_nRF54L15, both connection and communication can use coded-phy, so I expect proximity won’t be necessary for connection.
I’m examining the hr_coded samples, and what stands out as clearly different from the BLE code I’ve written before is that it seems to build the radio and app separately. I’m struggling to understand this.
Hi there,
So, Yes I would it is very different from how we used to think and build in Arduino.
On nRF52:
- BLE controller was:
- SoftDevice (precompiled), or
- Zephyr Controller (linked directly)
- It lived in the same firmware image
- PHY choice was mostly:
- Advertising PHY
- Connection PHY negotiated later
- Connection always started on 1M PHY
- Coded PHY was optional, secondary
In this new Realm, we Have 2 CPU’s so Two Power Domains that dictate.
On nRF54L15:
1. There are two CPUs / power domains
- CPUAPP → Your Zephyr application
- CPURADIO → Bluetooth Controller (SDC)
They are:
- Built separately
- Updated separately
- Powered independently
- Communicate over HCI (internal IPC)
This is intentional and architectural, allows more flexibility and some fault tolerance.
It is still one application.
What changed with nRF54L15 + SoftDevice Controller (SDC) is that the BLE controller is now a separately built firmware image that runs on a different CPU/power domain, even though you configure it from the same prj.conf.
You are not writing two apps, but you are building two images.
That’s the key distinction, the BLE controller is no longer “linked into your app”
Instead:
- Your app says:
CONFIG_BT=y
CONFIG_BT_SDC=y
CONFIG_BT_CTLR_PHY_CODED=y
- Sysbuild then:
- Builds your app
- Builds the SDC controller image
- Packages them together
- Flashes both
That looks like two apps, but functionally it is one system image.
I’ve learned from the dev academy courses the important part is On nRF54L15:
- The controller itself supports:
- Advertising on LE Coded
- Scanning on LE Coded
- Connecting on LE Coded
- The connection is established without ever touching 1M
So YES > “I expect proximity won’t be necessary for connection”
That expectation is correct. ![]()
It looks strange because the Sysbuild , builds both the Application image and the Controller image, the controller is configured for PHY at build time.
You still configure everything from prj.conf, but under the hood Zephyr builds two images:
- the application (CPUAPP)
- the SoftDevice Controller (CPURADIO)
Functionally it’s still one application, but architecturally the controller is now isolated for power, security, and performance.
This is why coded PHY can be used for both advertising and connection on nRF54L15, unlike nRF52 where connections always started on 1M PHY.
according to the Nords It was done to:
- Enable long-range connections
- Enable lower power idle
- Enable future multiprotocol
- Allow radio firmware updates without touching apps
- Prepare for nRF54 series security & partitioning
HTH
GL
PJ ![]()
They are planning a New SoftDevice update , end of 1st qtr 2026 with some pretty amazing includes so stay tuned..multiple concurrent connections, dual roles, etc…
Thanks for organizing the information.
I understand the two cores conceptually, but I still don’t grasp how to actually write the code. I suppose I’ll need to write a prj.conf file for each core. For now, since hr_coded is the only sample with proven results for coded-phy communication, I’m analyzing it in detail.