nRF24L01 – 工作原理、Arduino 接口、电路、代码

enter image description here

网上现成的教程很多,英文的据多,这篇我认为是入门最好的一篇,所以简单翻译一下

对于Arduino入门的教程,一般涉及传感器基础原理、电路搭建、参考代码

对于nRF24L01,我也是初步了解,并没有深入器件背后的原理,但是快速能够搭建展示的应用很关键,可以让我们立刻产生兴趣

在本教程中,我们将学习如何使用nRF24L01收发器模块在两个 Arduino 板之间进行无线通信。 nRF24L01 模块是使用 Arduino 时非常流行的无线通信选择

概述

为了解释无线通信,我们将举两个示例,第一个示例将从一个 Arduino 向另一个 Arduino 发送简单的“Hello World”消息,在第二个示例中,我们将在 Arduino 板之间进行双向通信,其中使用使用第一个 Arduino 上的操纵杆,我们将控制第二个 Arduino 上的伺服电机,反之亦然,使用第二个 Arduino 上的按钮,我们将控制第一个 Arduino 上的 LED。 enter image description here

nRF24L01 收发器模块

让我们仔细看看NRF24L01收发器模块。它使用 2.4 GHz 频段,可以以 250 kbps 至 2 Mbps 的波特率运行。如果在空旷的空间和较低的波特率下使用,其范围可达100米。

enter image description here 以下是完整的规格:

参数
频率范围 2.4 – 2.5GHz ISM 频段
数据速率 250Kbps/1Mbps/2Mbps
最大输出功率 0分贝
工作电压 1.9 – 3.6V
最大工作电流 12.3毫安
待机电流 22微安
逻辑输入 5V耐压
通讯范围 100m(空地)

怎么运行的

该模块可以使用 125 个不同的通道,从而可以在一个地方建立一个由 125 个独立工作的调制解调器组成的网络。每个通道最多可以有 6 个地址,或者每个单元可以同时与最多 6 个其他单元通信。 enter image description here

该模块在传输过程中的功耗仅为12mA左右,甚至比单个LED还要低。该模块的工作电压为1.9至3.6V,但好处是其他引脚可以承受5V逻辑,因此我们可以轻松地将其连接到Arduino,而无需使用任何逻辑电平转换器。 enter image description here 其中三个引脚用于 SPI 通信,需要将它们连接到 Arduino 的 SPI 引脚,但请注意,每个 Arduino 板都有不同的 SPI 引脚。引脚 CSN 和 CE 可以连接到 Arduino 板的任何数字引脚,用于将模块设置为待机或活动模式,以及在传输或命令模式之间切换。最后一个引脚是中断引脚,不必使用。

模块变体

NRF24L01 模块有多种变体。最流行的是带有板载天线的。这使得模块更加紧凑,但另一方面也将传输范围降低至100米左右的距离。

enter image description here 第二种变体,它没有板载天线,而是具有 SMA 连接器,我们可以在其上连接鸭形天线以获得更好的传输范围。

这里显示的第三种变体,除了鸭子天线之外,它还有一个 RFX2401C 芯片,其中包括PA(功率放大器)和LNA(低噪声放大器)。这可以放大 NRF24L01 信号,并在开放空间中实现更佳的传输范围,可达 1000 米。

我一般使用上图最右边的 那一种,也就是第三种变体,淘宝上很多这种,对于学习来说不算贵

nRF24L01 模块引脚分配

下面详细介绍了 NRF24L01 引脚排列以及 NRF24L01+ PA/LNA 模块。 enter image description here NRF24L01 和 NRF24L01+ PA/LNA 这两个模块具有相同的引脚排列,因此我们可以以相同的方式将它们连接到电路中。

如何将 nRF24L01 连接到 Arduino

enter image description here

正如我已经提到的,每个 Arduino 板都有不同的 SPI 引脚,因此在将模块连接到 Arduino 板时请记住这一点。

Arduino SCK 味噌 莫西 SS
UNO 13 12 11 10
NANO 13 12 11 10
MEGA 52 50 51 53

不同Arduino板上的SPI引脚

Arduino 和 nRF24L01 代码

一旦我们将 NRF24L01 模块连接到 Arduino 板,我们就可以为发射器和接收器编写代码。

首先我们需要下载并安装RF24库 ,这使得编程变得不那么困难。我们还可以直接从 Arduino IDE 库管理器安装该库。只需搜索“rf24”并找到并安装“TMRh20, Avamander”即可。

在Arduino IDE 库管理窗口可以搜索 RF24,找到对应的库,点击install即可,传统的方式是下载zip压缩档案,透过添加库include zip的方式安装第三方库;大部分库已经整合进Arduino IDE库管理,在线可以安装 enter image description here ctrl+shift+i 打开ARduino 库管理窗口

这是无线通信的两个代码,下面是它们的说明 - 发射器代码

    /*
* 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);
}

-接收器代码

*
* 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);
  }
}

代码说明

因此我们需要包含基本的 SPI 和新安装的 RF24 库并创建一个 RF24 对象。这里的两个参数是 CSN 和 CE 引脚

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

接下来,我们需要创建一个字节数组来表示地址,或者两个模块通过其进行通信的所谓管道。

const byte address[6] = "00001";

我们可以将此地址的值更改为任何 5 个字母的字符串,这样就可以选择我们要与哪个接收器通话,因此在我们的例子中,接收器和发射器都将具有相同的地址。

在设置部分,我们需要初始化无线电对象,并使用 radio.openWritingPipe() 函数设置我们将向其发送数据的接收器的地址,即我们之前设置的 5 个字母的字符串。

radio.openWritingPipe(address);

另一方面,在接收器处,使用 radio.setReadingPipe() 函数设置相同的地址,这样我们就可以实现两个模块之间的通信

radio.openReadingPipe(0, address);

然后使用 radio.setPALevel() 函数设置功率放大器级别,在我们的例子中,我将其设置为最小值,因为我的模块彼此非常接近。

radio.setPALevel(RF24_PA_MIN);

请注意,如果使用更高级别,建议在模块的 GND 和 3.3V 之间使用旁路电容,以便模块在工作时具有更稳定的电压。

接下来,我们有 radio.stopListening() 函数将模块设置为发射器,另一方面,我们有 radio.startListening() 函数将模块设置为接收器。

// at the Transmitter
radio.stopListening();


// at the Receiver
radio.startListening();

在发射器的循环部分中,我们创建一个字符数组,并为其分配消息“Hello World”。使用 radio.write() 函数,我们将该消息发送给接收者。这里的第一个参数是我们想要发送的变量

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

通过在变量名之前使用“&”,我们实际上设置了存储我们想要发送的数据的变量的地址,并使用第二个参数设置了我们想要从该变量获取的字节数。在这种情况下,sizeof() 函数获取字符串“text”的所有字节。在程序结束时我们将添加 1 秒延迟。

使用 radio.write() 函数,我们一次最多可以发送 32 个字节。

另一方面,在接收器处,在循环部分中使用 radio.available() 函数检查是否有数据要接收。如果这是真的,首先我们创建一个包含 32 个元素的数组,称为“text”,我们将在其中保存传入的数据。

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

使用 radion.read() 函数,我们读取数据并将其存储到“text”变量中。最后我们只是在串行监视器上打印文本。因此,一旦我们上传了这两个程序,我们就可以在接收器上运行串行监视器,我们会注意到每秒都会打印消息“Hello World”。

故障排除

值得注意的是,电源噪声是人们在尝试与 NRF24L01 模块成功通信时遇到的最常见问题之一。一般来说,射频电路或射频信号对电源噪声很敏感。因此,在电源线上添加一个去耦电容器始终是一个好主意。电容器可以是 10uF 到 100uF 之间的任何电容器。 enter image description here

另一个常见问题是 Arduino 板的 3.3V 引脚无法始终为 NRF24L01 模块提供足够的电源。因此,使用外部电源为模块供电也是一个好主意

与两个 NRF24L01 和 Arduino 进行双向无线通信

让我们看第二个示例,两个 Arduino 板之间的双向无线通信。这是电路原理图:

enter image description here

您可以从以下链接获取此示例所需的组件:

NRF24L01 收发器模块…………亚马逊 / Banggood / AliExpress Arduino 板……………………………………。 亚马逊 / Banggood /速卖通 操纵杆模块………………………………。亚马逊 / Banggood /速卖通 伺服电机……………………………………。亚马逊 / Banggood /速卖通 按钮……………………………………..亚马逊 / Banggood / AliExpress LED ………………………………………………亚马逊 / Banggood / AliExpress

这些组件从网上可以很容易购买到

nRF24L01 源代码 下面是这两个代码及其说明。

发射器代码

/*
* 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[][14] = {"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[][15] = {"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));
      }
    }

这里与前面的示例不同的是,我们需要创建两个管道或地址来进行双向通信。

const byte addresses[][16] = {"00001", "00002"};

在设置部分,我们需要定义两个管道,并注意第一个Arduino的写入地址需要是第二个Arduino的读取地址,反之亦然,第一个Arduino的读取地址需要是第二个Arduino的写入地址第二个Arduino

// at the Transmitter
radio.openWritingPipe(addresses[1]); // 00001
radio.openReadingPipe(1, addresses[0]); // 00002


// at the Receiver
radio.openWritingPipe(addresses[0]); // 00002
radio.openReadingPipe(1, addresses[1]); // 00001

在循环部分中,使用 radio.stopListening() 函数,我们将第一个 Arduino 设置为发射器,读取并映射操纵杆的值(从 0 到 180),然后使用 radio.write() 函数将数据发送到接收器。

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

另一方面,使用 radio.startListening() 函数,我们将第二个 Arduino 设置为接收器,并检查是否有可用数据。当有可用数据时,我们将读取它,将其保存到“angleV”变量中,然后使用该值来旋转伺服电机。

radio.startListening();
  if ( radio.available()) {
    while (radio.available()) {
      int angleV = 0;
      radio.read(&angleV, sizeof(angleV));
      myServo.write(angleV);
    }

接下来,在发送器处,我们将第一个 Arduino 设置为接收器,并使用一个空的“while”循环等待第二个 Arduino 发送数据,这就是按钮是否按下的状态数据。如果按下按钮,LED 将亮起。因此,这些过程不断重复,两个 Arduino 板不断发送和接收数据

示例 3 – 在单个包中发送多个变量

让我们看一下使用 NRF24L01 模块的另一个示例代码。除了我们构造和发送日期的方式之外,一切都与前面的示例相同。 - 发射器代码

/*
  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";

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte a = 0;
  byte b = 125;
  byte c = 255;
  int d = 1024;
  float e = 3.141592;
  String f = "Test";
};

Data_Package data; // Create a variable with the above structure

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

void loop() {
  // Send the whole data from the structure to the receiver
  radio.write(&data, sizeof(Data_Package));
  delay(500);
}

因此,我们可以创建一个结构体,它实际上是各种类型变量的集合。

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte a = 0;
  byte b = 125;
  byte c = 255;
  int d = 1024;
  float e = 3.141592;
  String f = "Test";
};

Data_Package data; // Create a variable with the above structure

我们应该记住,该结构数据的最大大小可以是 32 个字节。在这里我们可以看到我包含了三个字节类型的变量,一个整型变量(4 个字节),一个浮点变量(4 个字节)和一个包含四个字符的字符串(4 个字节)。总共 15 个字节。

  • 接收者代码
*
  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";

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte a = 0;
  byte b = 125;
  byte c = 255;
  int d = 1024;
  float e = 3.141592;
  String f = "Test";
};

Data_Package data; //Create a variable with the above structure

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

void loop() {
  // Check whether there is data to be received
  if (radio.available()) {
    radio.read(&data, sizeof(Data_Package)); // Read the whole data and store it into the 'data' structure
  }
  Serial.print("a: ");
  Serial.print(data.a);
  Serial.print(" b: ");
  Serial.print(data.b);
  Serial.print(" c: ");
  Serial.print(data.c);
  Serial.print(" d: ");
  Serial.print(data.d);
  Serial.print(" e: ");
  Serial.print(data.e);
  Serial.print(" f: ");
  Serial.println(data.f);
}

在接收方,我们必须定义相同的结构数据以便能够接收传入的数据。为了测试无线通信是否正常工作,我在串行监视器上打印了每个变量。 enter image description here

结论

当您的 Arduino 项目需要无线通信时,NRF24L01 模块是一个不错的选择。我已经在我的许多Arduino 项目中使用了这个模块。

我之前使用这个nRF24L01作了一个俄罗斯方块无线游戏手柄,很适合,传输没有任何延迟,不错 原文作者使用这个模块作了不少Arduino DIY项目,值得学习,很有启发 在这里我将列出我使用过这些模块的所有项目。

  • 使用 HC-05 蓝牙、NRF24L01 和 HC-12 收发器模块的 Arduino 机器人汽车无线控制
  • Arduino 无线气象站项目
  • DIY Arduino 遥控发射器
  • Arduino Ant 六足机器人
  • 基于 Arduino 的 DIY 遥控气垫船
  • Arduino 麦克纳姆轮机器人
  • 适用于 RC 模型和 Arduino 项目的 DIY Arduino RC 接收器
  • Arduino 遥控飞机 | 100% DIY 每个项目/教程都有如何使用 NRF24L01 模块的详细说明,包括电路图、改进的代码实现以实现更好的通信等。

“我最喜欢的项目是这个定制设计的 Arduino RC 发射器。它实际上是一个 14 通道 RC 控制器,可用于控制几乎任何 Arduino 项目。” enter image description here

评论