NRF24L01双Arduino通信实操:接线收发与供电坑

2026-11-08 13:24:50 31阅读 所属项目:ESP32 开发板

手头两块 Arduino,想让它们不拉线就互相说话,NRF24L01 大概是成本最低的答案:十几块钱一对,2.4GHz 收发一体,SPI 接口,官方 datasheet 摆在那,社区里的 TMRh20/RF24 库又把封装做得足够薄。这篇文章把 HowToMechatronics 那篇经典教程连同社区里反复出现的坑一起理了一遍——接线怎么接、收发对怎么写、地址和信道到底是什么关系,以及为什么你照抄代码却一个包都收不到。多数时候,问题不在代码,在供电。

先把模块认清楚:2.4GHz 收发一体的小板子

NRF24L01 是 Nordic Semiconductor 的 2.4GHz 无线收发芯片,常见的是带弹簧天线和晶振的转接板版本。工作电压 1.9 到 3.6V,发射电流约 12mA,速率 250kbps 到 2Mbps 可选,空旷环境标称距离 100 米上下。它有 125 个信道,每个信道可以开 6 条地址管道(pipe),也就是说一套网络里能挂 6 个节点同时收。

有个好消息:虽然模块供电是 3.3V,但它的逻辑引脚是 5V 容忍的,接 Arduino UNO 不用加电平转换,直接连。这点 datasheet 写得很清楚,新手可以少操一份心。市售版本里还有加 PA+LNA 放大的远距离版(带 SMA 天线,标称能到 1000 米),引脚定义不变,接线完全一样。

NRF24L01 2.4GHz 无线收发模块实拍

基于 NRF24L01 芯片的多种模块形态

接线:SPI 七根线一次接对

模块 8 个引脚:GND、VCC、CE、CSN、SCK、MOSI、MISO、IRQ。真正要接的是 7 根(IRQ 可悬空)。SPI 三根走硬件 SPI——UNO 上是 D11(MOSI)、D12(MISO)、D13(SCK);CE 和 CSN 是普通数字脚,库的构造函数里自己指定,教程用的是 D7 和 D8。VCC 必须接 3.3V,接 5V 会烧,这不是吓唬人,是芯片绝对最大额定值就到 3.6V。

接线对完之后,两块板子的接法完全一样,一块将来当发射、一块当接收,硬件不用改。

NRF24L01 与 Arduino 接线电路图

RF24 收发对:两段代码跑通第一个包

库用 Arduino IDE 库管理器搜 RF24 装 TMRh20 的那个就行。收发两端的关键点:地址必须一致,发射端 openWritingPipe 加 stopListening,接收端 openReadingPipe 加 startListening。单次 write 最大 32 字节,这是芯片硬件 FIFO 的限制,超了就得自己分包。

发射端,每秒发一个 Hello World:

/*
* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include "SPI.h"
#include "nRF24L01.h"
#include "RF24.h"

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  delay(1000);
}

接收端,串口 9600 打出来等着收:

/*
* Arduino Wireless Communication Tutorial
*       Example 1 - Receiver Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include "SPI.h"
#include "nRF24L01.h"
#include "RF24.h"

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}

烧完打开两边串口,接收端每秒一行 Hello World,链路就算通了。注意 openReadingPipe 第一个参数是管道号,0 到 5,tutorial 里用的是 0。

地址与信道:收不到包先查这两处

可以把信道想成频段,地址想成门牌号。两块模块必须落在同一个信道上,且收发的管道地址一字不差,才能握手。教程里用 5 字符地址 "00001",这个长度正好对应库默认的 5 字节地址宽度。如果一个端改了地址另一个没改,或者两端固件里地址字符串差一个字符,write 会照常返回,但接收端永远静默——这是新手最容易懵的地方,因为它不报错。

多节点时才是 pipe 真正的用武之地:一个接收端开 6 条不同地址的管道,可以同时监听 6 个发射端,不用自己写轮询。

NRF24L01 信道与地址工作原理示意

功率与供电:90% 的翻车都在这

先把功率级说清楚。setPALevel 从 RF24_PA_MIN 到 RF24_PA_MAX,拉高功率能拉开距离,但发射电流也随之上涨。第一次调试建议保持 MIN——距离近不影响你验证逻辑,还把电源压力降到最低。带 PA+LNA 的远距离版在 MAX 档瞬时电流能冲到上百毫安级别,这时候 Arduino 板载 3.3V 稳压器那点余量根本兜不住。

然后是全社区口径一致的头部坑:供电噪声。NRF24L01 对 3.3V 电源轨很敏感,USB 供电的 UNO 板载 3.3V 又常常不干净,症状五花八门——radio.begin() 打印全是 0、write 永远失败、时通时断。标准解法简单得不像话:在模块的 VCC 和 GND 之间并一颗 10 到 100 微法的电解电容,大量翻车案例就此终结。如果还不稳,就别指望板载 3.3V 了,外部独立 3.3V 稳压(AMS1117 之类)给模块单独供电,共地即可。

NRF24L01 供电去耦电容与外接电源示意

顺带一提天线版本的选择:普通板载天线版够用的场景就别上 PA+LNA,功率越大越考验供电,很多买了远距离版的人反而因为电源跟不上,表现不如普通版。

NRF24L01 PA+LNA 远距离版引脚图

进阶:双向通信,一块板子又发又收

RF24 允许运行中在收发之间快速切换,做法是给两端各开两条地址,互相把对方的地址挂在自己的读管道上,然后每轮 loop 里 stopListening 发一包、再 startListening 听一包。下面是教程 Example 2 的完整代码:发射端读 A0 电位器映射成舵机角度发过去,接收端驱动舵机,同时把手上的按键状态回传,发射端据此点亮自己的 LED。

发射端:

/*
* Arduino Wireless Communication Tutorial
*     Example 2 - Transmitter Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include "SPI.h"
#include "nRF24L01.h"
#include "RF24.h"

#define led 12

RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
boolean buttonState = 0;

void setup() {
  pinMode(12, OUTPUT);
  radio.begin();
  radio.openWritingPipe(addresses[1]); // 00002
  radio.openReadingPipe(1, addresses[0]); // 00001
  radio.setPALevel(RF24_PA_MIN);
}

void loop() {
  delay(5);

  radio.stopListening();
  int potValue = analogRead(A0);
  int angleValue = map(potValue, 0, 1023, 0, 180);
  radio.write(&angleValue, sizeof(angleValue));

  delay(5);
  radio.startListening();
  while (!radio.available());
  radio.read(&buttonState, sizeof(buttonState));
  if (buttonState == HIGH) {
    digitalWrite(led, HIGH);
  }
  else {
    digitalWrite(led, LOW);
  }
}

接收端:

/*
* Arduino Wireless Communication Tutorial
*     Example 2 - Receiver Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include "SPI.h"
#include "nRF24L01.h"
#include "RF24.h"
#include "Servo.h"

#define button 4

RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
Servo myServo;
boolean buttonState = 0;

void setup() {
  pinMode(button, INPUT);
  myServo.attach(5);
  radio.begin();
  radio.openWritingPipe(addresses[0]); // 00001
  radio.openReadingPipe(1, addresses[1]); // 00002
  radio.setPALevel(RF24_PA_MIN);
}

void loop() {
  delay(5);
  radio.startListening();
  if ( radio.available()) {
    while (radio.available()) {
      int angleV = 0;
      radio.read(&angleV, sizeof(angleV));
      myServo.write(angleV);
    }
    delay(5);
    radio.stopListening();
    buttonState = digitalRead(button);
    radio.write(&buttonState, sizeof(buttonState));
  }
}

双 Arduino 双向通信电路连接图

想一次传多路数据(摇杆 XY、按键、旋钮),就把字段打包进一个 struct,两端定义完全一致,radio.write 直接 write 整个结构体,只要 sizeof 不超过 32 字节就行。这是把 NRF24L01 用成小型无线传感器网络的基础。

收尾:这套链路能干什么

跑通收发对之后,往上看一层,这十几块钱的链路能干的事不少:遥控小车、无线传感器节点、多机编队的底层通道,都是同一个套路变着花样用。它不像 ESP32 那样自带 WiFi 和协议栈,但正因为它够裸、够简单、不依赖路由器,延迟和时序都在自己手里,反而是做确定性通信更好的起点。把供电这一课学扎实,NRF24L01 会是你工具箱里性价比最高的一对模块。


作者:野生极客一枚
链接:https://www.aiddithome.com/p/94ab8f358b162.html
本作版权归作者所有,未经允许请勿转载。若存在违规行为,您可以点击 “举报”。


登录 后发表评论
4条评论
下班才有空
1楼 · 10小时前

请问双向通信那段 delay(5) 是必须的吗,去掉会丢包吗?打算给遥控小车用

老张装机
2楼 · 10小时前

前年玩过一阵这模块,最后就是栽在供电上。USB 供电的 UNO 带不动,模块时不时掉线,当时排查了两天没头绪,后来在 VCC 和 GND 之间并了颗 100uF 电容立马就稳了。文章里说 90% 翻车在供电,一点不夸张,这坑我拿血泪填过。

胶佬憨憨
3楼 · 10小时前

地址差一个字符不报错这个点真的关键,我第一次玩的时候就是发射端写 00001 接收端写成 00002,write 还返回成功,接收端死活没输出,折腾一下午。

只看不说话
4楼 · 10小时前

电容一颗治百病,这模块老玩家都懂