Enhanced Shockburst (ESB) with 4 Mbps PHY on the BL54L15

Introduction

This application note demonstrates how to achieve wireless throughput beyond what Bluetooth Low Energy (BLE) can deliver by using Nordic's Enhanced Shockburst (ESB) protocol with the proprietary 4 Mbps PHY available on the nRF54 series. The implementation targets two Ezurio BL54L15 DVKs running nRF Connect SDK v3.2.4 and achieves approximately 3.3 Mbps of sustained unidirectional throughput – well above the practical ceiling of BLE's 2 Mbps PHY.

The complete demo project is available on GitHub: BL54x-High-Throughput-ESB-Demo-with-4Mbps-Phy

Enhanced Shockburst Overview

Enhanced Shockburst is a lightweight, packet-based radio protocol provided by Nordic Semiconductor as part of their radio peripheral. It operates in the 2.4 GHz ISM band, the same spectrum as Bluetooth, but it is not Bluetooth. Understanding a few key differences helps frame why ESB is useful for high-throughput, low-latency links.

ESB vs. Bluetooth Low Energy

AspectBLEESB
Protocol stack Full stack: L2CAP, ATT/GATT, GAP, etc. Minimal: link-layer only, no stack overhead
Connection management Connection intervals, supervision timeouts, channel maps No connection concept; TX fires when application writes a packet
Channel usage Adaptive frequency hopping across 37 data channels Single fixed channel (configurable)
Packet overhead Significant (L2CAP headers, ATT headers, encryption) Minimal (preamble, address, PCF, CRC)
Max PHY rate 2 Mbps (LE 2M PHY) 4 Mbps (nRF54 proprietary PHY)
Practical throughput ~1.4 Mbps at best with LE 2M ~3.3 Mbps with 4 Mbps PHY, no ACK
Interoperability Any BLE device Nordic-to-Nordic only
Acknowledging Built into link layer Optional (per-packet configurable)

BLE is designed for interoperability and power-efficient communication across many device vendors. That versatility comes at the cost of protocol overhead that reduces effective throughput. ESB strips communication down to the bare essentials: the application hands a payload to the radio, and the radio transmits it. There is no connection negotiation, no frequency hopping, and no mandatory acknowledgement. This makes ESB a good fit when both ends of the link run Nordic hardware and the use case demands raw throughput or minimal latency over interoperability.

ESB Packet Structure

An ESB packet consists of:

  • Preamble (1 byte) -- bit pattern for receiver synchronization
  • Address (3-5 bytes) -- base address + prefix to identify the logical pipe
  • Packet Control Field (PCF) -- contains payload length (6 bits for dynamic payloads), PID (2 bits), and a no-acknowledge flag (1 bit)
  • Payload (0-252 bytes) -- application data
  • CRC (1-2 bytes) -- error detection

With Dynamic Payload Length (DPL) enabled, the receiver reads the length field from the PCF to determine how many bytes to expect, allowing variable-size payloads up to the configured maximum.

Hardware and Software Setup

  • Hardware: 2x Ezurio BL54L15 DVK (based on Nordic nRF54L15)
  • SDK: nRF Connect SDK v3.2.4
  • Build system: Zephyr / CMake with sysbuild
  • Projects:

    • esb_4mbps_ptx -- transmitter (Primary TX)
    • esb_4mbps_prx -- receiver (Primary RX)

Both projects are derived from the standard ESB sample applications shipped with the nRF Connect SDK.

Building the Projects

The recommended way to build and flash these projects is through Visual Studio Code with the nRF Connect for VS Code extension. The extension provides a graphical interface for configuring build targets, selecting boards, and flashing firmware without needing to use the command line.

To get started:

  1. Install VS Code and the nRF Connect for VS Code extension pack, which includes the nRF Connect SDK and toolchain manager.
  2. Open either esb_4mbps_ptx or esb_4mbps_prx as an application in the nRF Connect extension sidebar.
  3. Create a build configuration and select the BL54L15 DVK as the target board.
  4. Build the project and flash it to the connected DVK.
  5. Repeat for the second project on the second DVK.

For detailed setup instructions including SDK and toolchain installation, refer to the Ezurio Quick Start Guide - BL54Lxx Series Application Development with Nordic nRF Connect for VS Code.

Code Walkthrough

Both PTX and PRX follow the same high-level structure:

  1. Start the HF clock (required for radio operation)
  2. Initialize LEDs (for visual status)
  3. Configure and initialize ESB
  4. Enter the main loop

The key differences lie in the ESB configuration (TX vs. RX mode) and how the main loop operates.

Transmitter (PTX)

ESB initialization (esb_initialize in esb_4mbps_ptx/src/main.c):
struct esb_config config = ESB_LEGACY_CONFIG;

config.protocol = ESB_PROTOCOL_ESB_DPL;    // Dynamic payload length
config.retransmit_delay = 600;
config.bitrate = ESB_BITRATE_4MBPS;         // 4 Mbps PHY
config.event_handler = event_handler;
config.mode = ESB_MODE_PTX;                 // Transmitter role
config.selective_auto_ack = true;           // Per-packet ACK control
config.use_fast_ramp_up = true;             // Reduced radio ramp-up time

Payload setup:

tx_payload.noack = true;    // Disable acknowledgement for this payload

The payload is configured with a length of 252 bytes (the maximum supported by the ESB implementation) to maximize data per packet and therefore throughput.

Main loop:

while (1) {
    if (ready) {
        ready = false;
        esb_flush_tx();
        err = esb_write_payload(&tx_payload);
        // ...
    }
    k_yield();
}
The transmitter uses a tight polling loop. A ready flag, set by the ESB event handler upon ESB_EVENT_TX_SUCCESS, gates each new transmission. esb_flush_tx() clears any stale entries from the TX FIFO before writing the next payload. k_yield() gives the Zephyr scheduler an opportunity to run other tasks (such as logging) without blocking on a sleep timer.

Event handler:

On ESB_EVENT_TX_SUCCESS, the handler accumulates the number of bytes sent and sets the ready flag so the main loop can queue the next packet.

Receiver (PRX)

ESB initialization (esb_initialize in esb_4mbps_prx/src/main.c):The configuration mirrors the PTX side, with the mode set to ESB_MODE_PRX. After initialization, the receiver starts listening:
esb_start_rx();

Event handler:

On ESB_EVENT_RX_RECEIVED, the handler reads the received payload from the RX FIFO and accumulates the byte count:
case ESB_EVENT_RX_RECEIVED:
    if (esb_read_rx_payload(&rx_payload) == 0) {
        bytes_received_total += rx_payload.length;
    }
    break;

Main loop:

The PRX main loop does not interact with the radio directly. It simply polls the elapsed time and computes throughput at a fixed interval:

while (1) {
    uint32_t current_timestamp = k_uptime_get();
    uint32_t duration_ms = current_timestamp - last_timestamp;
    if (duration_ms >= PRINT_INTERVAL) {
        // compute and log throughput
    }
    k_yield();
}

Key Modifications for 4 Mbps Operation

The following changes were made relative to the original nRF Connect SDK ESB sample applications to enable 4 Mbps throughput:

1. Bitrate Selection

config.bitrate = ESB_BITRATE_4MBPS;
The original samples default to ESB_BITRATE_2MBPS or ESB_BITRATE_1MBPS. The 4 Mbps PHY is a proprietary mode available on nRF54 series devices.

2. Disabling Packet Acknowledgement

config.selective_auto_ack = true;
tx_payload.noack = true;           // PTX side only
The combination of selective_auto_ack in the config and noack = true on each payload fully disables the ACK/retransmit cycle. In the default configuration, the transmitter waits for an acknowledgement from the receiver after every packet and retransmits on failure. Disabling this eliminates the turnaround time between packets and allows back-to-back transmission. See the Limitations section for trade-offs.

3. Fast Radio Ramp-Up

config.use_fast_ramp_up = true;
Enabled via CONFIG_ESB_FAST_SWITCHING=y in prj.conf. This reduces the radio ramp-up time before each transmission or reception, shaving dead time between packets.

4. Maximum Payload Length

In prj.conf:
CONFIG_ESB_MAX_PAYLOAD_LENGTH=252

The default ESB configuration caps payloads at 32 bytes. Increasing this to 252 bytes (the protocol maximum) is critical for throughput: larger payloads amortize the fixed per-packet overhead (preamble, address, PCF, CRC) over more data bytes.

Note that the original sample applications use the ESB_CREATE_PAYLOAD(...) macro to compose payloads, but this macro only supports up to 63 bytes of data. To use the full 252-byte payload, the esb_payload struct must be initialized manually:
static struct esb_payload tx_payload = {
    .pipe = 0,
    .length = 252,
    .data = { 0, 0, 0, ... }   // 252 bytes
};

5. Continuous Transmission Loop

The original PTX sample uses k_sleep() between transmissions, which throttles throughput. The modified version replaces this with a tight k_yield() loop that queues a new packet as soon as the previous one completes, maximizing radio utilization.

6. Floating-Point Printf Support

CONFIG_CBPRINTF_FP_SUPPORT=y
Enables %f format specifiers in LOG_INF() for printing throughput values. Not related to radio performance but required for the throughput reporting.

Throughput Calculation

Both PTX and PRX compute throughput using the same approach. Byte counters are accumulated in the ESB event handler (interrupt context) and periodically evaluated in the main loop.

Accumulation (in the event handler):

  • PTX: on ESB_EVENT_TX_SUCCESS, adds tx_payload.length to bytes_sent_total
  • PRX: on ESB_EVENT_RX_RECEIVED, adds rx_payload.length to bytes_received_total

Calculation (in the main loop, every 10 seconds):

double duration_sec = duration_ms / 1000.0;
double throughput = (bytes * 8) / (duration_sec * 1000000.0);
LOG_INF("Throughput: %.2f Mbps", throughput);

The formula converts accumulated bytes to bits, divides by the elapsed time in seconds, and expresses the result in megabits per second (Mbps). Counters are reset after each measurement interval.

The 10-second interval (PRINT_INTERVAL = 10000 ms) provides a stable average that smooths out any jitter from logging or scheduling overhead.

Throughput Results

The screenshot below shows the PTX terminal (left) and PRX terminal (right) running simultaneously on two BL54L15 DVKs:

image-20260421-111108.png

The transmitter reports a consistent 3.30 Mbps sent, while the receiver reports approximately 3.27-3.28 Mbps received. The small delta between TX and RX is expected: the transmitter counts every packet written to the radio, while the receiver only counts packets that were successfully received and read from the FIFO. At 4 Mbps over the air with no acknowledgement, occasional packet loss is normal.

Limitations

Inherent ESB Limitations

  • No frequency hopping. ESB operates on a single channel. In a congested 2.4 GHz environment (Wi-Fi, Bluetooth, microwave ovens), sustained interference on that channel will degrade throughput or cause packet loss with no automatic mitigation. BLE's adaptive frequency hopping handles this transparently.
  • Nordic-to-Nordic only. ESB is a proprietary protocol. Both ends of the link must use Nordic radio hardware. This rules out communication with phones, tablets, or third-party wireless devices.
  • No built-in security. ESB provides no encryption or authentication at the protocol level. If the link carries sensitive data, encryption must be implemented at the application layer.
  • Single-direction optimized. While ESB supports bidirectional communication through ACK payloads, it is fundamentally asymmetric: one device is PTX (initiates transfers), the other is PRX (responds). Simultaneous bidirectional streaming is not a native mode.
  • Limited range. The 4 Mbps PHY trades sensitivity for speed. Higher data rates generally reduce the effective range compared to 1 Mbps or 2 Mbps modes. For range-critical deployments, the link budget should be evaluated carefully.

Limitations from Disabling Acknowledgement

This implementation disables packet acknowledgement (noack = true) to maximize throughput. This has several consequences:
  • No delivery guarantee. The transmitter has no way of knowing whether a packet was received. Lost packets are silently dropped. This is evident in the throughput measurements: the PTX reports 3.30 Mbps sent while the PRX reports ~3.27 Mbps received -- the difference represents lost packets.
  • No automatic retransmission. With ACKs enabled, ESB automatically retransmits failed packets up to retransmit_count times. Without ACKs, the application must implement its own error detection and recovery if data integrity is required.
  • No flow control. ACK packets can carry payloads from PRX back to PTX, providing a basic backchannel. Without ACKs, the PRX has no way to signal the PTX to slow down or report status.
  • Not suitable for all payloads. For use cases that require reliable delivery (firmware updates, command/control), acknowledgement should be re-enabled at the cost of reduced throughput. The no-ACK mode is best suited for streaming scenarios where occasional data loss is acceptable -- for example, continuous sensor data where the next sample supersedes the previous one.
For applications that need both high throughput and reliable delivery, a hybrid approach is possible: use selective_auto_ack to send the bulk of the data without ACKs while periodically sending an acknowledged control packet to verify link health and synchronize sequence numbers for application-level error recovery.

License

The sample applications in this project are derived from the ESB examples included in the nRF Connect SDK and are licensed under the Nordic 5-Clause License (LicenseRef-Nordic-5-Clause). The full license text is available at:

<https://github.com/nrfconnect/sdk-nrf/blob/main/LICENSE >