Is BLE + raw IEEE 802.15.4 TX (PHY only, no Thread/Zigbee) officially supported on nRF52840 with MPSL?

Hello Nordic team,

I am working on an nRF52840 project using nRF Connect SDK (Zephyr-based, v2.x / v3.x) and I would like to clarify the official support status of a specific multiprotocol use case.

I have intentionally limited this question to two focused points to avoid ambiguity.

1. Target Use Case

  • Device: nRF52840

  • SDK: nRF Connect SDK (Zephyr)

  • Protocols:

    • Bluetooth LE (SoftDevice Controller)

    • IEEE 802.15.4 PHY only (raw TX)

      • No Thread

      • No Zigbee

      • No MAC / NET stack usage

  • Radio sharing via MPSL (dynamic multiprotocol)

The 802.15.4 side is used only for custom raw frame transmission (driver-level TX), not for any standardized 802.15.4-based stack.


2. Observations So Far

  • BLE and IEEE 802.15.4 TX can both be enabled and initialized.

  • A single 802.15.4 TX call works while BLE is active.

  • However, periodic or continuous TX loops do not behave reliably:

    • TX returns success (0)

    • But repeated transmissions do not appear on a sniffer

    • No explicit runtime error is reported

  • All development was done using the nRF IEEE 802.15.4 radio driver + MPSL, not the legacy raw radio HAL.


3. Documentation Gap

From available documentation and samples, I understand that:

  • nRF52840 officially supports BLE + Thread/Zigbee concurrency via MPSL.

  • Multiprotocol support is clearly documented for full 802.15.4 stacks.

  • However, I cannot find an explicit statement or reference confirming:

    BLE + custom raw IEEE 802.15.4 TX (PHY only) is officially supported and validated


4. Questions (Primary Scope)

  1. Is BLE + raw IEEE 802.15.4 TX (PHY only, no Thread/Zigbee) an officially supported use case on nRF52840 when using MPSL?

  2. If supported in principle, are there any known limitations or design constraints (e.g. scheduling, TX frequency, timeslot behavior) that would explain why periodic TX may silently stop or not appear on a sniffer?

I am intentionally not asking for code review or full implementation guidance yet.
I would like to first understand the official support boundary and expectations.


5. Next Steps

Depending on the answers to the two questions above, I plan to:

  • Adjust the architecture (e.g. move to Thread/Zigbee, or dedicated timeslot handling), or

  • Continue debugging within the supported configuration and ask follow-up implementation questions.

Thank you for your time and clarification.

Best regards,

Ryan

  • Hello,

    You should be able to use 802154_phy_test together with MPSL+BLE. It is only BLE that has hard timing requirements, while the rest of the time can be spent on 802154. We don't have any samples showing exactly how to do this, but I imagine that you can take any of the ble+Zigbee/Thread samples, strip out the zigbee/thread, and replace it with what you find in NCS\nrf\samples\peripheral\802154_phy_test, and go from there.

    For follow up questions, please don't use chatGPT or other AI tools to format your questions. It makes it difficult to know what you are really asking for, and what the AI made up.

    Best regards,

    Edvin

  • Hello.

    I understand that there is no official reference code available for this specific use case.
    As a first step, I am trying to implement periodic TX using IEEE 802.15.4 only, but even this basic periodic transmission is not working yet.

    If I upload the source code I have been working on, would it be possible to receive support or feedback on it?

    Best regards,

    Ryan

  • You can upload it, and I can try to run it. Please let me know what NCS version you are using.

    Best regards,

    Edvin

  • Hello.

    [My test Environment]

    boards: nRF52840DK

    SDK: ncs v3.1.2 Toolchain: v3.2.1

    The code below is a test application that periodically transmits 802.15.4 frames.
    It was written to send periodic broadcast packets (dst: 0xFFFF) without ACK, but no packets are actually transmitted and the transmit callback functions are never called.

    Best regards,

    Ryan

    [main.c]
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/sys/printk.h>
    #include <nrf_802154.h>
    
    LOG_MODULE_REGISTER(step_a_tx, LOG_LEVEL_INF);
    
    static volatile bool tx_done = true;
    static volatile uint32_t tx_success_count = 0;
    static volatile uint32_t tx_fail_count = 0;
    
    void mpsl_assert_handle(const char *const file, const uint32_t line)
    {
        printk("MPSL ASSERT: %s:%u\n", file, line);
        k_panic();
    }
    
    void nrf_802154_transmitted_raw(
        uint8_t *p_frame,
        const nrf_802154_transmit_done_metadata_t *p_metadata)
    {
        ARG_UNUSED(p_frame);
        ARG_UNUSED(p_metadata);
        tx_success_count++;
        printk("!!! TX SUCCESS #%u !!!\n\n", tx_success_count);
        tx_done = true;
    }
    
    void nrf_802154_transmit_failed(
        uint8_t *p_frame,
        nrf_802154_tx_error_t error,
        const nrf_802154_transmit_done_metadata_t *p_metadata)
    {
        ARG_UNUSED(p_frame);
        ARG_UNUSED(p_metadata);
        tx_fail_count++;
        printk("!!! TX FAILED: error=%d #%u !!!\n\n", error, tx_fail_count);
        tx_done = true;
    }
    
    void nrf_802154_received_raw(uint8_t *p_data, int8_t power, uint8_t lqi)
    {
        nrf_802154_buffer_free_raw(p_data);
    }
    
    void nrf_802154_receive_failed(nrf_802154_rx_error_t error, uint32_t id)
    {
        ARG_UNUSED(error);
        ARG_UNUSED(id);
    }
    
    void nrf_802154_cca_done(bool channel_free)
    {
        ARG_UNUSED(channel_free);
    }
    
    void nrf_802154_cca_failed(nrf_802154_cca_error_t error)
    {
        ARG_UNUSED(error);
    }
    
    void nrf_802154_energy_detected(const nrf_802154_energy_detected_t *p_result)
    {
        ARG_UNUSED(p_result);
    }
    
    void nrf_802154_energy_detection_failed(nrf_802154_ed_error_t error)
    {
        ARG_UNUSED(error);
    }
    
    int main(void)
    {
        printk("\n===========================================\n");
        printk("IEEE 802.15.4 TX Test - FINAL ATTEMPT\n");
        printk("NCS v3.2.1\n");
        printk("===========================================\n\n");
        
        k_msleep(500);
        
        printk("Init...\n");
        nrf_802154_init();
        k_msleep(100);
        
        nrf_802154_channel_set(20);
        nrf_802154_tx_power_set(0);
        nrf_802154_promiscuous_set(true);
        nrf_802154_sleep();
        k_msleep(100);
        
        printk("Ready!\n\n");
        
        static uint8_t tx_pkt[128];
        uint8_t seq = 0;
        
        while (1) {
            if (tx_done) {
                tx_done = false;
                
                uint8_t idx = 0;
                
                // min frame: PHR + FCF(no ACK) + Seq + Payload
                tx_pkt[idx++] = 5;        // Length: FCF(2) + Seq(1) + FCS(2) = 5
                tx_pkt[idx++] = 0x01;     // FCF low: 0x01 (Beacon frame, simplest)
                tx_pkt[idx++] = 0x00;     // FCF high: 0x00
                tx_pkt[idx++] = seq;      // Sequence
                // FCS auto add
                
                printk("TX #%u: ", seq);
                
                // Metadata - init
                nrf_802154_transmit_metadata_t metadata = {
                    .frame_props = {
                        .is_secured = false,
                        .dynamic_data_is_set = true 
                    },
                    .cca = false,
                    .tx_power = {
                        .use_metadata_value = false,
                        .power = 0
                    },
                    .tx_channel = {
                        .use_metadata_value = false,
                        .channel = 20
                    },
                    .tx_timestamp_encode = false
                };
                
                nrf_802154_tx_error_t err = nrf_802154_transmit_raw(tx_pkt, &metadata);
                
                if (err != NRF_802154_TX_ERROR_NONE) {
                    printk("ERROR %d\n\n", err);
                    tx_done = true;
                } else {
                    printk("Queued...\n");
                    
                    int timeout = 1000;
                    int waited = 0;
                    
                    while (!tx_done && waited < timeout) {
                        k_msleep(50);
                        waited += 50;
                    }
                    
                    if (!tx_done) {
                        printk("TIMEOUT\n\n");
                        tx_done = true;
                    }
                }
                
                seq++;
            }
            
            if (seq % 10 == 0 && seq > 0) {
                printk("Stats: Success=%u, Failed=%u\n\n", 
                       tx_success_count, tx_fail_count);
            }
            
            k_sleep(K_SECONDS(1));
        }
        
        return 0;
    }
    [prj.conf]
    # Logging
    CONFIG_LOG=y
    CONFIG_LOG_DEFAULT_LEVEL=3
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_BACKEND_UART=n
    CONFIG_RTT_CONSOLE=y
    CONFIG_PRINTK=y
    CONFIG_LOG_MODE_IMMEDIATE=y
    
    # IEEE 802.15.4 Raw API
    CONFIG_NRF_802154_RADIO_DRIVER=y
    
    # MPSL
    CONFIG_MPSL=y
    CONFIG_MPSL_ASSERT_HANDLER=y
    CONFIG_NRF_802154_TEMPERATURE_UPDATE=n
    
    # Stack sizes
    CONFIG_MAIN_STACK_SIZE=4096
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
    CONFIG_ISR_STACK_SIZE=2048
    
    #IRQ
    CONFIG_ZERO_LATENCY_IRQS=y
    
    # Assert
    CONFIG_ASSERT=y
    CONFIG_ASSERT_VERBOSE=y
    [log]
    00> *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    00> *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    00> 
    00> ===========================================
    00> IEEE 802.15.4 TX Test - FINAL ATTEMPT
    00> NCS v3.2.1
    00> ===========================================
    00> 
    00> Init...
    00> Ready!
    00> 
    00> TX #0: Queued...
    00> TIMEOUT
    00> 
    00> TX #1: ERROR 1
    00> 
    00> TX #2: ERROR 1
    00> 
    00> TX #3: ERROR 1
    00> 
    00> TX #4: ERROR 1
    00> 
    00> TX #5: ERROR 1
    00> 
    00> TX #6: ERROR 1
    00> 
    00> TX #7: ERROR 1
    00> 
    00> TX #8: ERROR 1
    00> 
    00> TX #9: ERROR 1
    00> 
    00> Stats: Success=0, Failed=0

  • Did you test the sample in my previous reply? Are you able to send the 0xFFFF packets there?

    Best regards,

    Edvin

Related