树莓派4与MicroPython环境下Raspberry Pi Pico间NRF24L01通信故障求助
Raspberry Pi 4与Raspberry Pi Pico的NRF24L01无线通信故障排查
我正尝试使用两块NRF24L01模块搭建无线通信系统,采用Raspberry Pi 4 Model B作为一端(使用pyRF24库),Raspberry Pi Pico运行MicroPython作为另一端。此前用RPi4搭配RPi Zero(均用pyRF24库)通信完全正常,但替换为Pico后,无论哪端作为发送/接收,均无法传输数据,且无错误提示。已多次检查引脚连接:
MISO: GP4 MOSI: GP7 SCK: GP6 CSN: GP15 CE: GP14
测试代码
Raspberry Pi 4 Model B代码(基于pyRF24库)
""" 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()
Raspberry Pi Pico代码(使用MicroPython的nrf24l01库)
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)
可能的故障原因
- 射频参数不匹配:RPi4端设置了
RF24_PA_MAX,但Pico端MicroPython库默认PA级别可能不同;两端未明确设置同一频道,pyRF24默认频道为76,部分MicroPython库默认频道可能不一致。 - SPI配置或库实现差异:Pico端
stop_listening无参数,需确认库是否正确设置了TX地址;虽然SPI模式设置为0(匹配RPi4),但需检查硬件电平兼容性。 - 电源供应不足:NRF24L01发射时峰值电流较高,Pico的3.3V引脚可能无法提供足够电流,导致模块无法正常工作。
- 地址/管道配置隐性问题:虽然表面地址匹配,但需确认库对地址的处理是否一致(比如是否自动截断或补全地址长度)。
Pico上使用MicroPython驱动NRF24L01的注意细节
- 电源稳定性优先:给NRF24L01单独接3.3V电源,并添加100uF滤波电容,避免使用Pico的3.3V引脚直接供电。
- 强制统一射频参数:手动设置两端的频道、PA级别、数据速率,确保完全一致(比如频道设为40,PA级别设为MAX,数据速率设为1Mbps)。
- 选择可靠的库:使用维护活跃的MicroPython NRF24L01库,避免过时库的功能缺失或bug。
- 确认SPI引脚无冲突:Pico的SPI引脚可能被其他功能占用,需确保使用的引脚未被复用为其他外设。
进一步调试建议
- 打印寄存器配置:RPi4端启用
radio.print_pretty_details(),Pico端添加代码读取并打印模块寄存器值,对比两端配置是否一致。 - 测试单向通信并检查返回值:固定一端发送固定字节(如
b"test"),另一端监听,同时检查Pico端send()方法的返回值,确认是否发送成功。 - 更换频道测试:手动设置两端为同一非WiFi干扰频道(如40),排除频道冲突问题。
- 硬件连接排查:用万用表检查SPI引脚连接是否牢固,CE/CSN引脚电平是否在对应模式下正常变化。
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

