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
| Aspect | BLE | ESB |
|---|---|---|
| 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:
- Install VS Code and the nRF Connect for VS Code extension pack, which includes the nRF Connect SDK and toolchain manager.
- Open either
esb_4mbps_ptxoresb_4mbps_prxas an application in the nRF Connect extension sidebar. - Create a build configuration and select the BL54L15 DVK as the target board.
- Build the project and flash it to the connected DVK.
- 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:
- Start the HF clock (required for radio operation)
- Initialize LEDs (for visual status)
- Configure and initialize ESB
- 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 timePayload setup:
tx_payload.noack = true; // Disable acknowledgement for this payloadThe 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:
OnESB_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:
OnESB_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 onlyThe 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
Inprj.conf:CONFIG_ESB_MAX_PAYLOAD_LENGTH=252The 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 theESB_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 usesk_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=yEnables %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, addstx_payload.lengthtobytes_sent_total - PRX: on
ESB_EVENT_RX_RECEIVED, addsrx_payload.lengthtobytes_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:

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_counttimes. 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.
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:/filters:background_color(white)/2024-01/BL54L15-Group.png)