使用 NRF24L01 模块构建强健的 Arduino 无线 Mesh 网络
概述
在本教程中,我们将学习如何构建一个由多个 NRF24L01 收发模块组成的 Arduino 无线网络。你可以观看下方视频或阅读以下图文教程。
作为示例,我搭建了一个包含 5 个节点的网络,每个节点都能与网络中的任意节点通信,同时可同时作为发射器和接收器工作。此示例的设置方式可扩展为更大规模网络——精确地说,我们可以在单个 RF 信道上让多达 3125 个模块相互通信。下面我们来看看它是如何工作的。

在之前的教程中,我们已经学习了如何使用 NRF24L01 模块和 RF24 库实现两块 Arduino 板之间的无线通信。现在,除了该库,我们还将使用 RF24Network 库,它能以简便的方式构建多块板卡互联的 Arduino 无线网络。以下是网络拓扑的工作原理。
多 NRF24L01 模块通信
单个 NRF24L01 模块可同时主动监听多达 6 个其他模块。

RF24Network 库利用这一能力生成树形拓扑网络:一个节点为基站(根节点),所有其他节点或为基站的子节点,或为其他节点的子节点。每个节点最多可有 5 个子节点,深度可达 5 层,因此总共可创建 3125 个节点的网络。每个节点必须用一个 15 位地址定义,该地址精确描述了节点在树中的位置。

我们可以用八进制格式定义节点地址。基站/主节点地址为 00,基站子节点地址为 01~05,节点 01 的子节点地址为 011~051,以此类推。
注意:若节点 011 要与节点 02 通信,通信必须经过节点 01 和基站节点 00,因此这两个节点必须始终保持活跃,通信才能成功。

基于 RF24Network 库的 Arduino 无线舵机控制
在讲解本教程的主要示例前,为更好理解库的工作原理,我们先用两块 Arduino 板通信做一个更简单的示例。以下是该示例的电路图。

你可以从以下链接获取本教程所需组件:
- NRF24L01 收发模块
- 舵机
- 电位器
- Arduino Nano
我们将使用第一块 Arduino 上的电位器控制第二块 Arduino 上的舵机。现在来看源代码。
电位器端代码(节点 00)
/* Arduino 无线网络 - 多 NRF24L01 教程 == 示例 01 - 舵机控制 / 节点 00 - 电位器 == by Dejan, www.HowToMechatronics.com
Libraries: nRF24/RF24, https://github.com/nRF24/RF24
nRF24/RF24Network, https://github.com/nRF24/RF24Network */
#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>
RF24 radio(10, 9); // nRF24L01 (CE,CSN)
RF24Network network(radio); // 将 radio 加入网络
const uint16_t this_node = 00; // 八进制格式的节点地址
const uint16_t node01 = 01;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node); // (信道, 节点地址)
}
void loop() {
network.update();
unsigned long potValue = analogRead(A0); // 读取电位器值
unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // 转换为 0-180 适合舵机
RF24NetworkHeader header(node01); // 目标节点地址
bool ok = network.write(header, &angleValue, sizeof(angleValue)); // 发送数据
}
首先需包含 RF24、RF24Network 和 SPI 库。创建 RF24 对象并将其加入 RF24Network 对象。用八进制定义节点地址:本节点 00,舵机端节点 01。setup 中初始化网络,设置信道和本节点地址。loop 中不断调用 update() 处理网络动作,读取电位器并映射为 0-180,创建网络头指定目标地址,最后用 write() 发送数据。
舵机端代码(节点 01)
/* Arduino 无线网络 - 多 NRF24L01 教程 == 示例 01 - 舵机控制 / 节点 01 - 舵机 == */
#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>
#include <Servo.h>
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 01;
Servo myservo;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
myservo.attach(3); // 舵机引脚
}
void loop() {
network.update();
while ( network.available() ) {
RF24NetworkHeader header;
unsigned long incomingData;
network.read(header, &incomingData, sizeof(incomingData));
myservo.write(incomingData);
}
}
舵机端同样定义库和对象,地址为 01。loop 中用 while 循环和 available() 检查是否有数据到达,若有则创建头对象和变量存储数据,用 read() 读取并存入 incomingData,最后用该数据驱动舵机转动。
多 NRF24L01 模块的 Arduino 无线网络
理解上述示例后,我们进入本教程主示例:构建 5 块 Arduino 互通的无线网络。以下是示例框图。

从基站出发:电位器控制节点 01 的舵机,第二电位器控制节点 022 的 LED,按钮控制节点 012 的 LED,基站的 LED 由节点 02 的电位器控制;节点 012 的红外传感器控制节点 01 的 LED。这展示了如何同时收发数据,以及如何跨分支通信。下面看各节点代码。
基站 00 源代码
/* Arduino 无线网络 - 多 NRF24L01 教程 == 基站/主节点 00 == by Dejan, www.HowToMechatronics.com
Libraries: nRF24/RF24, https://github.com/nRF24/RF24
nRF24/RF24Network, https://github.com/nRF24/RF24Network */
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define button 2
#define led 3
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 00;
const uint16_t node01 = 01;
const uint16_t node012 = 012;
const uint16_t node022 = 022;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
radio.setDataRate(RF24_2MBPS);
pinMode(button, INPUT_PULLUP);
pinMode(led, OUTPUT);
}
void loop() {
network.update();
//===== 接收 =====//
while ( network.available() ) {
RF24NetworkHeader header;
unsigned long incomingData;
network.read(header, &incomingData, sizeof(incomingData));
analogWrite(led, incomingData); // PWM 调光
}
//===== 发送 =====//
// 节点 01 舵机控制
unsigned long potValue = analogRead(A0);
unsigned long angleValue = map(potValue, 0, 1023, 0, 180);
RF24NetworkHeader header2(node01);
bool ok = network.write(header2, &angleValue, sizeof(angleValue));
// 节点 012 LED 控制
unsigned long buttonState = digitalRead(button);
RF24NetworkHeader header4(node012);
bool ok3 = network.write(header4, &buttonState, sizeof(buttonState));
// 节点 022 LED 控制
unsigned long pot2Value = analogRead(A1);
RF24NetworkHeader header3(node022);
bool ok2 = network.write(header3, &pot2Value, sizeof(pot2Value));
}
基站定义库、对象及所有将发送数据的目标节点。loop 先检查接收数据:若有则读取并用于控制 LED 亮度(数据来自节点 02 电位器)。随后发送三路数据:电位器 A0 控制节点 01 舵机,按钮控制节点 012 LED,电位器 A1 控制节点 022 LED。
节点 02 源代码
/* Arduino 无线网络 - 多 NRF24L01 教程 == 节点 02 (主节点 00 的子节点) == */
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 02;
const uint16_t master00 = 00;
const unsigned long interval = 10; // ms
unsigned long last_sent;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
radio.setDataRate(RF24_2MBPS);
}
void loop() {
network.update();
//===== 发送 =====//
unsigned long now = millis();
if (now - last_sent >= interval) {
last_sent = now;
unsigned long potValue = analogRead(A0);
unsigned long ledBrightness = map(potValue, 0, 1023, 0, 255);
RF24NetworkHeader header(master00);
bool ok = network.write(header, &ledBrightness, sizeof(ledBrightness));
}
}
节点 02 每 10ms 读取电位器,映射为 0-255 PWM 值发送给基站 00。
节点 01 源代码
/* Arduino 无线网络 - 多 NRF24L01 教程 == 节点 01 (主节点 00 的子节点) == */
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Servo.h>
#define led 2
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 01;
const uint16_t master00 = 00;
Servo myservo;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
radio.setDataRate(RF24_2MBPS);
myservo.attach(3);
pinMode(led, OUTPUT);
}
void loop() {
network.update();
//===== 接收 =====//
while ( network.available() ) {
RF24NetworkHeader header;
unsigned long incomingData;
network.read(header, &incomingData, sizeof(incomingData));
if (header.from_node == 0) { // 来自主节点
myservo.write(incomingData);
}
if (header.from_node == 10) { // 来自节点 012 (八进制 012 = 十进制 10)
digitalWrite(led, !incomingData);
}
}
}
节点 01 同时接收来自基站(舵机控制)和节点 012(LED 控制)的数据,通过 header.from_node 判断来源并分别处理。
节点 012 源代码
/* Arduino 无线网络 - 多 NRF24L01 教程 == 节点 012 (节点 01 的子节点) == */
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define led 2
#define IR 3
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 012;
const uint16_t node01 = 01;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
radio.setDataRate(RF24_2MBPS);
pinMode(led, OUTPUT);
pinMode(IR, INPUT);
}
void loop() {
network.update();
//===== 接收 =====//
while ( network.available() ) {
RF24NetworkHeader header;
unsigned long buttonState;
network.read(header, &buttonState, sizeof(buttonState));
digitalWrite(led, !buttonState);
}
//===== 发送 =====//
unsigned long irV = digitalRead(IR);
RF24NetworkHeader header8(node01);
bool ok = network.write(header8, &irV, sizeof(irV));
}
节点 012 既接收基站按钮数据控制本地 LED,又发送红外传感器数据给节点 01 控制其 LED。
节点 022 源代码
/* Arduino 无线网络 - 多 NRF24L01 教程 == 节点 022 (节点 02 的子节点) == */
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define led1 2
#define led2 3
#define led3 4
#define led4 5
RF24 radio(10, 9);
RF24Network network(radio);
const uint16_t this_node = 022;
const uint16_t master00 = 00;
void setup() {
SPI.begin();
radio.begin();
network.begin(90, this_node);
radio.setDataRate(RF24_2MBPS);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(led4, OUTPUT);
}
void loop() {
network.update();
//===== 接收 =====//
while ( network.available() ) {
RF24NetworkHeader header;
unsigned long potValue;
network.read(header, &potValue, sizeof(potValue));
// 根据电位器值点亮 LED
if (potValue > 240) digitalWrite(led1, HIGH); else digitalWrite(led1, LOW);
if (potValue > 480) digitalWrite(led2, HIGH); else digitalWrite(led2, LOW);
if (potValue > 720) digitalWrite(led3, HIGH); else digitalWrite(led3, LOW);
if (potValue > 960) digitalWrite(led4, HIGH); else digitalWrite(led4, LOW);
}
}
节点 022 接收基站第二电位器数据,根据阈值控制 4 个 LED 亮灭。
总结:若硬件连接正确且所有节点保持活跃,我们只需精确寻址节点,所有繁重的网络路由工作都由强大的 RF24Network 库自动完成。
希望你喜欢这个 Arduino 项目并学到了新知识。欢迎在评论区提问。
制造工艺
- 打造低成本家庭无线传感器网络:Arduino + 树莓派 + Xively 物联网平台
- 树莓派手写数字识别:TensorFlow 与 OpenCV 实战部署
- 基于 Infineon 3D 磁传感器的 Arduino 无线磁通讯实验
- K30 CO₂ 传感器:室内空气质量实时监测
- 基于 1Sheeld 与 Arduino 的盲聋人士沟通辅助系统
- 智能存钱罐:Arduino Nano 控制硬币识别器实现余额显示
- Arduino + HC-05 蓝牙控制 LED:MIT App Inventor 手机 App 开发全攻略
- DFRobot 电容指纹传感器 (ID809) 接入 Arduino/ESP8266 完整教程
- USB-BLE 无线 MIDI 适配器三种方案:让乐器无线连接 iOS 设备
- 基于 Arduino Mega 与 Nextion 显示屏的交互式气象站
- Arduino Mega 驱动 Wii Nunchuk 控制 6 自由度机械臂
- 智能无线开关控制器:Arduino + 433MHz 远程操控墙壁开关