nRF24L01をマスター:無線通信、Arduino連携、回路図・コード完全ガイド
このチュートリアルでは、nRF24L01トランシーバーモジュールを使用して、2枚のArduinoボード間で無線通信を実現する方法を学びます。nRF24L01モジュールは、Arduinoで無線通信を行う際の非常に人気のある選択肢です。 私はこのモジュールを多数のArduinoプロジェクトで使用してきました。いくつかはこちらで確認できます: 以下の動画を見るか、書面チュートリアルをお読みください。nRF24L01トランシーバーモジュールについて知っておくべきすべて(ピンアウト、動作原理、配線、複数のコード例)が含まれています。 無線通信を説明するために2つの例を作成します。最初の例では、1台のArduinoからもう1台へシンプルな「Hello World」メッセージを送信します。2番目の例では、Arduinoボード間で双方向通信を行います。1台目のArduinoのジョイスティックで2台目のサーボモーターを制御し、逆に2台目のプッシュボタンで1台目のLEDを制御します。 nRF24L01トランシーバーモジュールを詳しく見てみましょう。2.4GHz帯を使用し、250kbpsから2Mbpsのボーレートで動作できます。開放空間で低ボーレートを使用すれば、通信距離は最大100メートルに達します。 完全な仕様は以下の通りです: このモジュールは125の異なるチャネルを使用でき、1か所に125台の独立して動作するモデムを構築できます。各チャネルは最大6つのアドレスを持つことができ、つまり各ユニットは同時に最大6台の他ユニットと通信可能です。 このモジュールの消費電力は送信時でわずか約12mAで、単一のLEDよりも低いです。動作電圧は1.9〜3.6Vですが、他のピンは5Vロジックに耐えられるため、ロジックレベルコンバーターなしでArduinoに簡単に接続できます。 3つのピンはSPI通信用で、ArduinoのSPIピンに接続する必要があります。ただし、ArduinoボードごとにSPIピンが異なることに注意してください。CSNピンとCEピンはArduinoの任意のデジタルピンに接続でき、モジュールをスタンバイ/アクティブモードに設定したり、送信/コマンドモードを切り替えたりするために使用します。最後のピンは割り込みピンで、必ずしも使用する必要はありません。 nRF24L01モジュールにはいくつかのバリエーションがあります。最も人気なのは、オンボードアンテナ搭載タイプです。これによりモジュールがコンパクトになりますが、反面、伝送距離が約100メートルに制限されます。 2つ目のバリエーションは、オンボードアンテナの代わりにSMAコネクタを備え、ダックアンテナを取り付けて伝送距離を延ばせます。 ここに示す3つ目のバリエーションは、ダックアンテナに加えて、PA(パワーアンプ)とLNA(低雑音アンプ)を含むRFX2401Cチップを搭載しています。これによりnRF24L01信号が増幅され、開放空間で最大1000メートルの伝送距離を実現します。 nRF24L01ピンアウトと、nRF24L01+ PA/LNAモジュールの詳細を見てみましょう。 nRF24L01とnRF24L01+ PA/LNAの両モジュールは同じピンアウトを持つため、同じように回路接続できます。 nRF24L01モジュールをArduinoボードに接続する方法は以下の通りです。 前述の通り、ArduinoボードごとにSPIピンが異なるため、自分のボードに合わせて接続してください。 このArduinoチュートリアルに必要なコンポーネントは以下のリンクから入手できます: nRF24L01モジュールをArduinoボードに接続したら、送信側と受信側両方のコードを作成します。 まず、プログラミングを容易にするRF24ライブラリをダウンロードしてインストールする必要があります。Arduino IDEのライブラリマネージャーから直接インストールすることも可能です。「rf24」で検索し、「TMRh20, Avamander」によるものを探してインストールしてください。 以下に無線通信用の2つのコードとその説明を示します。 基本のSPIライブラリと新しくインストールしたRF24ライブラリをインクルードし、RF24オブジェクトを作成します。2つの引数はCSNピンとCEピンです。 次に、2つのモジュールが通信するためのアドレス、いわゆる「パイプ」を表すバイト配列を作成します。 このアドレスの値は任意の5文字の文字列に変更でき、どの受信機と通信するかを選択できます。今回は送信側と受信側で同じアドレスを使用します。 setupセクションでは、radioオブジェクトを初期化し、radio.openWritingPipe()関数で送信先受信機のアドレス(先ほど設定した5文字の文字列)を設定します。 一方、受信側ではradio.openReadingPipe()関数で同じアドレスを設定し、2つのモジュール間の通信を有効にします。 次に、radio.setPALevel()関数でパワーアンプレベルを設定します。ここではモジュール同士が非常に近いため、最小に設定します。 より高いレベルを使用する場合は、モジュールのGNDと3.3V間にバイパスコンデンサを入れ、動作中の電圧を安定させることを推奨します。 次に、radio.stopListening()関数でモジュールを送信モードに、受信側ではradio.startListening()関数で受信モードに設定します。 loopセクションで、送信側では送信したいメッセージ「Hello World」を文字配列に代入し、radio.write()関数で受信側へ送信します。第1引数は送信したい変数です。 変数名の前に「&」を付けることで、送信したいデータを格納する変数のアドレスを指定し、第2引数でその変数から何バイト取得するかを指定します。ここではsizeof()関数で文字列「text」の全バイト数を取得しています。最後に1秒の遅延を入れます。 radio.write()関数では、最大32バイトまで一度に送信可能です。 受信側では、loopセクションでradio.available()関数を使って受信データがあるか確認します。真であれば、まず32要素の配列「text」を作成し、受信データを格納します。 radio.read()関数でデータを読み取り、「text」変数に格納します。最後にシリアルモニタに出力します。両方のプログラムをアップロードし、受信側でシリアルモニタを開くと、毎秒「Hello World」が表示されるのが確認できます。 電源ノイズは、nRF24L01モジュールで通信を成功させようとする際に最もよく遭遇する問題の一つです。一般的に、RF回路や無線周波数信号は電源ノイズに敏感です。したがって、電源ラインにデカップリングコンデンサを入れるのが常に良いアイデアです。コンデンサは10µF〜100µF程度で構いません。 もう一つのよくある問題は、Arduinoボードの3.3Vピンが、nRF24L01モジュールに十分な電力を供給できない場合があることです。外部電源でモジュールに給電するのも良い対策です。 2つ目の例、2台のArduinoボード間の双方向無線通信を見てみましょう。回路図は以下の通りです: この例に必要なコンポーネントは以下のリンクから入手できます: 以下に2つのコードとその説明を示します。 送信側コード 受信側コード 前回の例と異なるのは、双方向通信のために2つのパイプ(アドレス)を作成する必要がある点です。 setupセクションで両方のパイプを定義し、1台目のArduinoの書き込みアドレスが2台目の読み込みアドレスになり、逆に1台目の読み込みアドレスが2台目の書き込みアドレスになることに注意してください。 loopセクションで、radio.stopListening()関数を使って1台目のArduinoを送信モードにし、ジョイスティックの値を0〜1023から0〜180にマッピングし、radio.write()関数でデータを受信側へ送信します。 受信側では、radio.startListening()関数で2台目のArduinoを受信モードにし、データがあるか確認します。データがある間は読み取り、「angleV」変数に保存し、その値でサーボモーターを回転させます。 次に、送信側で1台目のArduinoを受信モードにし、空の「while」ループで2台目からデータが送られるのを待ちます。これはプッシュボタンが押されているかどうかの状態データです。ボタンが押されていればLEDが点灯します。このプロセスが常に繰り返され、両Arduinoボードが継続的にデータを送受信します。 nRF24L01モジュールを使用したもう一つのコード例を見てみましょう。前回の例とすべて同じですが、データの構造化と送信方法が異なります。 送信側コード 構造体(struct)を作成できます。これはさまざまな型の変数のコレクションです。 この構造体データの最大サイズは32バイトであることに注意してください。ここではbyte型変数3つ、int型変数1つ(4バイト)、float型変数1つ(4バイト)、4文字のString(4バイト)を含めており、合計15バイトです。 受信側コード 受信側でも同じ構造体を定義することで、受信データを正しく解釈できます。無線通信が正常に動作するかテストするため、各変数をシリアルモニタに出力しました。 nRF24L01モジュールは、Arduinoプロジェクトで無線通信が必要な際の優れた選択肢です。私はこのモジュールを多くのArduinoプロジェクトで使用してきました。 私がこれまでこのモジュールを使用したプロジェクトをすべてリストアップします。 これらのプロジェクト/チュートリアルそれぞれに、nRF24L01モジュールの使用方法、回路図、通信改善のための改良コード実装などの詳細な説明があります。 私のお気に入りプロジェクトは、このカスタム設計のArduino RC送信機です。実際には14チャネルのRCコントローラーで、ほぼあらゆるArduinoプロジェクトの制御に使用できます。 このチュートリアルは以上です。お楽しみいただけたし、何か新しいことを学んでいただけたら幸いです。いつものように、ご質問があれば下のコメント欄でお気軽にお尋ねください。
概要

nRF24L01トランシーバーモジュール

周波数範囲 2.4 – 2.5GHz ISM帯 データレート 250Kbps / 1Mbps / 2Mbps 最大出力電力 0dBm 動作電圧 1.9 – 3.6V 最大動作電流 12.3mA スタンバイ電流 22µA ロジック入力 5V耐圧 通信距離 100m(開放空間) 動作原理


モジュールのバリエーション

nRF24L01モジュールピンアウト

nRF24L01をArduinoに接続する方法

Arduino SCK MISO MOSI SS Uno 13 12 11 10 Nano 13 12 11 10 Mega 52 50 51 53
ArduinoとnRF24L01のコード
送信側コード
/*
* Arduino無線通信チュートリアル
* 例1 - 送信側コード
* 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無線通信チュートリアル
* 例1 - 受信側コード
* 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);
}
}コード解説
RF24 radio(7, 8); // CE, CSNconst byte address[6] = "00001";radio.openWritingPipe(address);radio.openReadingPipe(0, address);radio.setPALevel(RF24_PA_MIN);// 送信側で
radio.stopListening();// 受信側で
radio.startListening();void loop() {
const char text[] = "Hello World";
radio.write(&text, sizeof(text));
delay(1000);
}void loop() {
if (radio.available()) {
char text[32] = "";
radio.read(&text, sizeof(text));
Serial.println(text);
}
}トラブルシューティング

2つのnRF24L01とArduinoによる双方向無線通信

nRF24L01ソースコード
/*
* Arduino無線通信チュートリアル
* 例2 - 送信側コード
* 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無線通信チュートリアル
* 例2 - 受信側コード
* 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));
}
}const byte addresses[][6] = {"00001", "00002"};// 送信側で
radio.openWritingPipe(addresses[1]); // 00001
radio.openReadingPipe(1, addresses[0]); // 00002// 受信側で
radio.openWritingPipe(addresses[0]); // 00002
radio.openReadingPipe(1, addresses[1]); // 00001radio.stopListening();
int potValue = analogRead(A0);
int angleValue = map(potValue, 0, 1023, 0, 180);
radio.write(&angleValue, sizeof(angleValue));radio.startListening();
if (radio.available()) {
while (radio.available()) {
int angleV = 0;
radio.read(&angleV, sizeof(angleV));
myServo.write(angleV);
}例3 – 単一パッケージで複数変数を送信
/* Arduino無線通信チュートリアル 例1 - 送信側コード 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";
// この構造体の最大サイズは32バイト - nRF24L01バッファ制限
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; // 上記構造体の変数を作成
void setup() {
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}
void loop() {
// 構造体から受信側へデータ全体を送信
radio.write(&data, sizeof(Data_Package));
delay(500);
}// この構造体の最大サイズは32バイト - nRF24L01バッファ制限
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; // 上記構造体の変数を作成/* Arduino無線通信チュートリアル 例1 - 受信側コード 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";
// この構造体の最大サイズは32バイト - nRF24L01バッファ制限
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; // 上記構造体の変数を作成
void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
}
void loop() {
// 受信データがあるか確認
if (radio.available()) {
radio.read(&data, sizeof(Data_Package));
// データ全体を読み取り、'data'構造体に格納
}
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);
}
結論

製造プロセス
- OpenSensorsで空気質を測定: ArduinoとPythonによるガイド
- ArduinoでユニバーサルIRリモコンを自作:ステップバイステップガイド
- デジタル身長計の自作:Arduino Nanoとレーザー距離センサーで成長記録
- JX Wave Generator – Arduino互換DDS発振器(OLEDディスプレイ搭載)
- Arduino、Google Cardboard、Bluetoothで作るDIY VRスケートボード
- Arduino MegaとNEO-6M GPSモジュールの接続:ステップバイステップガイド
- サーミスタを使った温度測定: Arduinoステップバイステップガイド
- Arduinoで楽器演奏:音階生成のステップバイステップガイド
- ArduinoでModbusをマスターする:ステップバイステップガイド
- ArduRadio:Arduino自作FMラジオ付き目覚まし時計
- 脳・コンピューターインターフェース:思考を行動に再接続