I’m trying to set up wireless communication using two NRF24L01 modules. I’m using a Raspberry Pi 4 Model B as the receiver using the pyRF24 Library and a Raspberry Pi Pico with MicroPython as the sender.
I tried setup with the Raspberry Pi 4 and a Raspberry Pi Zero (both with NRF24L01) first and it worked perfectly - I was able to send and receive data using the mentioned pyRF24 Library and the example programs ran without any issues.
Now, I replaced the Raspberry Pi Zero with the Raspberry Pi Pico. For the Pico, I tried several MicroPython libraries (e.g., from the official micropython GitHub repository) and double-checked the pin connections multiple times:
MISO: GP4
MOSI: GP7
SCK: GP6
CSN: GP15
CE: GP14
Now, the communication no longer works. There are no error messages, but no data is received, regardless of whether the Pico is set as sender or receiver. I will add the testing code for the Pi 4 (based on the first example in the pyRF24 library and the Pico (tried to adapt the same example for the micropython-library) at the end of this text.
What might cause this? Am I using the micropython library wrong? Are there any specifics to using the NRF24L01 with MicroPython on the Pico that I should be aware of? How to further debug this?
Code for Raspberry Pi 4 Model B, based on PyRF24 Documentation:
"""
Simple example of using the RF24 class.
See documentation at https://nRF24.github.io/pyRF24
"""
import time
import struct
from pyrf24 import RF24, RF24_PA_MAX, RF24_DRIVER
print(__file__) # print example name
########### USER CONFIGURATION ###########
# CE Pin uses GPIO number with RPi and SPIDEV drivers, other drivers use
# their own pin numbering
# CS Pin corresponds the SPI bus number at /dev/spidev<a>.<b>
# ie: radio = RF24(<ce_pin>, <a>*10+<b>)
# where CS pin for /dev/spidev1.0 is 10, /dev/spidev1.1 is 11 etc...
CSN_PIN = 0 # aka CE0 on SPI bus 0: /dev/spidev0.0
if RF24_DRIVER == "MRAA":
CE_PIN = 15 # for GPIO22
elif RF24_DRIVER == "wiringPi":
CE_PIN = 3 # for GPIO22
else:
CE_PIN = 22
radio = RF24(CE_PIN, CSN_PIN)
# 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]
# For this example, we will use different addresses
# An address need to be a buffer protocol object (bytearray)
address = [b"1Node", b"2Node"]
# It is very helpful to think of an address as a path instead of as
# an identifying device destination
# 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)
)
# initialize the nRF24L01 on the spi bus
if not radio.begin():
raise OSError("nRF24L01 hardware isn't responding")
# set the Power Amplifier level to -12 dBm since this test example is
# usually run with nRF24L01 transceivers in close proximity of each other
radio.set_pa_level(RF24_PA_MAX) # RF24_PA_MAX is default
# set TX address of RX node (uses pipe 0)
radio.stop_listening(address[radio_number]) # enter inactive TX mode
# set RX address of TX node into an RX pipe
radio.open_rx_pipe(1, address[not radio_number]) # using pipe 1
# To save time during transmission, we'll set the payload size to be only what
# we need. A float value occupies 4 bytes in memory using struct.calcsize()
# "<f" means a little endian unsigned float
radio.payload_size = struct.calcsize("<f")
# for debugging
# radio.print_details()
# or for human readable data
# radio.print_pretty_details()
def master(count: int = 5): # count = 5 will only transmit 5 packets
"""Transmits an incrementing float every second"""
radio.listen = False # ensures the nRF24L01 is in TX mode
while count:
# use struct.pack() to pack your data into a usable payload
# 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 = radio.write(buffer)
end_timer = time.monotonic_ns() # end timer
if not result:
print("Transmission failed or timed out")
else:
print(
"Transmission successful! Time to Transmit:",
f"{(end_timer - start_timer) / 1000} us. Sent: {payload[0]}",
)
payload[0] += 0.01
time.sleep(1)
count -= 1
# recommended behavior is to keep radio in TX mode while idle
radio.listen = False # enter inactive TX mode
def slave(timeout: int = 6):
"""Polls the radio and prints the received value. This method expires
after 6 seconds of no received transmission."""
radio.listen = True # put radio into RX mode
start = time.monotonic()
while (time.monotonic() - start) < timeout:
has_payload, pipe_number = radio.available_pipe()
if has_payload:
length = radio.payload_size # grab the payload length
# fetch 1 payload from RX FIFO
received = radio.read(length)
# expecting a little endian float, thus the format string "<f"
# received[:4] truncates padded 0s in case dynamic payloads are disabled
payload[0] = struct.unpack("<f", received[:4])[0]
# print details about the received packet
print(f"Received {length} bytes on pipe {pipe_number}: {payload[0]}")
start = time.monotonic() # reset the timeout timer
# recommended behavior is to keep radio in TX mode while idle
radio.listen = False # enter inactive TX mode
if radio_number == 1:
slave()
else:
master()
Code for Raspberry Pi Pico, using the micropython nrf24l01.py library:
from machine import Pin, SPI
import time, struct
from nrf24l01 import NRF24L01
# SPI & Pins
spi = SPI(0, baudrate=4000000, polarity=0, phase=0, sck=Pin(6), mosi=Pin(7), miso=Pin(4))
csn = Pin(15, mode=Pin.OUT)
ce = Pin(14, mode=Pin.OUT)
nrf = NRF24L01(spi, csn, ce, payload_size=4)
pipes = (b"1Node", b"2Node")
# Select role: 0 = sender, 1 = reciever
ROLE = 0
if ROLE == 0: # Master / Sender
print("Pipe to send: ", pipes[0])
print("Pipe to recieve: ", pipes[1])
nrf.open_tx_pipe(pipes[0])
nrf.open_rx_pipe(1, pipes[1])
nrf.stop_listening()
value = 0.0
while True:
buf = struct.pack("<f", value)
nrf.send(buf)
print("Sent:", value)
value += 0.01
time.sleep(1)
else: # Slave / Reciever
print("Pipe to send: ", pipes[1])
print("Pipe to recieve: ", pipes[0])
nrf.open_tx_pipe(pipes[1])
nrf.open_rx_pipe(1, pipes[0])
nrf.start_listening()
while True:
if nrf.any():
buf = nrf.recv()
value = struct.unpack("<f", buf)[0]
print("Received:", value)