Arduino Nano RP2040 + CircuitPython + RF24 (NRF24L01+) problem


I'm trying to use 2 Arduino Nano RP2040 Connect boards to transmit and receive data wirelessly with NRF24L01+ chips.

Arduino: link

NRF24L01+ chips: link

I'm using this CircuitPython library, I drag the "circuitpython_nrf24l01" folder onto the root of my CIRCUITPY drive. My file is roughly the library's "examples/" (hardcoding if I'm using it in transmit or receive mode)...

Simple example of using the RF24 class.
import time
import struct
import board
import digitalio

# if running this on a ATSAMD21 M0 based board
# from circuitpython_nrf24l01.rf24_lite import RF24
from circuitpython_nrf24l01.rf24 import RF24

# change these (digital output) pins accordingly
ce = digitalio.DigitalInOut(board.D4)
csn = digitalio.DigitalInOut(board.D5)

# using board.SPI() automatically selects the MCU's
# available SPI pins, board.SCK, board.MOSI, board.MISO
spi = board.SPI()  # init spi bus object

# we'll be using the dynamic payload size feature (enabled by default)
# initialize the nRF24L01 on the spi bus object
nrf = RF24(spi, csn, ce)

# set the Power Amplifier level to -12 dBm since this test example is
# usually run with nRF24L01 transceivers in close proximity

nrf.pa_level = -12

# addresses needs to be in a buffer protocol object (bytearray)
address = [b"1Node", b"2Node"]

# to use different addresses on a pair of radios, we need a variable to
# uniquely identify which address this radio will use to transmit
# 0 uses address[0] to transmit, 1 uses address[1] to transmit

# radio_number = bool(
#     int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0)
# )
radio_number = 0

# set TX address of RX node into the TX pipe
nrf.open_tx_pipe(address[radio_number])  # always uses pipe 0

# set RX address of TX node into an RX pipe
nrf.open_rx_pipe(1, address[not radio_number])  # using pipe 1

# using the python keyword global is bad practice. Instead we'll use a 1 item
# list to store our float number for the payloads sent
payload = [0.0]

# uncomment the following 3 lines for compatibility with TMRh20 library
# nrf.allow_ask_no_ack = False
# nrf.dynamic_payloads = False
# nrf.payload_length = 4

def transmit_data(count=1000):
    """Transmits an incrementing integer every second"""
    nrf.listen = False  # ensures the nRF24L01 is in TX mode

    while count:
        # use struct.pack to packetize your data
        # into a usable payload
        buffer = struct.pack("<f", payload[0])
        # "<f" means a single little endian (4 byte) float value.
        start_timer = time.monotonic_ns()  # start timer
        result = nrf.send(buffer)
        end_timer = time.monotonic_ns()  # end timer
        if not result:
            print("send() failed or timed out")
                "Transmission successful! Time to Transmit: "
                "{} us. Sent: {}".format(
                    (end_timer - start_timer) / 1000, payload[0]
            payload[0] += 0.01
        count -= 1

def receive_data(timeout=1000):
    """Polls the radio and prints the received value. This method expires
    after 6 seconds of no received transmission"""
    nrf.listen = True  # put radio into RX mode and power up

    start = time.monotonic()
    while (time.monotonic() - start) < timeout:
        if nrf.available():
            # grab information about the received payload
            payload_size, pipe_number = (nrf.any(), nrf.pipe)
            # fetch 1 payload from RX FIFO
            buffer =  # also clears nrf.irq_dr status flag
            # expecting a little endian float, thus the format string "<f"
            # buffer[:4] truncates padded 0s if dynamic payloads are disabled
            payload[0] = struct.unpack("<f", buffer[:4])[0]
            # print details about the received packet
                "Received {} bytes on pipe {}: {}".format(
                    payload_size, pipe_number, payload[0]
            start = time.monotonic()

    # recommended behavior is to keep in TX mode while idle
    nrf.listen = False  # put the nRF24L01 is in TX mode

if __name__ == "__main__":
        # receive_data()
    except KeyboardInterrupt:
        print(" Keyboard Interrupt detected. Powering down radio...")
        nrf.power = False
    print("    Run slave() on receiver\n    Run master() on transmitter")

The transmitter keeps giving the error "send() failed or timed out"

My debugging....

  • I've tested my RF24 chips on regular Arduino Uno R3 boards, and they talk just fine using the official RF24 library

  • I've attached the Fritzing diagram for the regular Arduino Uno boards

  • here's the Arduino Uno R3 transmitter's code:

* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
* by Dejan Nedelkovski,
* Library: TMRh20/RF24,

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN

const byte address[6] = "00001";

void setup() {

void loop() {
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  • here's the Arduino Uno R3 receiver's code:
* Arduino Wireless Communication Tutorial
*       Example 1 - Receiver Code
* by Dejan Nedelkovski,
* Library: TMRh20/RF24,

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN

const byte address[6] = "00001";

void setup() {
  radio.openReadingPipe(0, address);

void loop() {
  if (radio.available()) {
    char text[32] = "";, sizeof(text));

I'd really appreciate any help in getting this to work on my new Arduino Nano RP2040 Connect boards!! I really want to make them into remote controllers for my robotics project!

Thank you,

Fritzing for Arduino Uno:

Fritzing for Arduino Nano RP2040 Connect: