RaspberryとArduinoの無線通信

nRF24L01のEnhanced ShockBurst機能


nRF24L01とその後継機種であるnRF24L01+は安価($1程度)ですが、非常に高機能な無線モジュールです。

それぞれのドキュメントはこちらに公開されています。

nRF24L01のドキュメント
https://www.mouser.com/datasheet/2/297/nRF24L01_Product_Specification_v2_0-9199.pdf

nRF24L01+のドキュメント
https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf

どちらのドキュメントも非常に読みごたえが有るドキュメントです。
ライブラリを使ってなんとなく動きが分かったので、ざっくりと読んでみました。

nRF24L01もnRF24L01+も、3つの送信用FIFOバッファと3つの受信用FIFOバッファを持っています。
3つまでの送信データをモジュール側に書き込んでおけば、あとは勝手にモジュールが送信してくれます。
また受信データも3つまでならばモジュール側で保持してくれます。

nRF24L01もnRF24L01+も、送信モードと受信モードがあり、一方向通信を前提としていますが、
Enhanced ShockBurstという機能で相手にデータが届いたかどうかを判定することができます。
ドキュメントの多くのページは、ほとんどこのEnhanced ShockBurstについて記載されています。



ベースとなるShockBurstでは以下の機能を提供しています。
・ShockBurst RXでは、有効なアドレスとペイロードをそれぞれ受信したときにIRQでMCUに通知します。
・ShockBurst TXでは、送信が完了したことをIRQでMCUに通知します。
IRQを監視することで、MCUは受信したこと、送信が完了したことを知ることができます。

Enhanced ShockBurstは上記に加え、以下の機能を提供しています。

プライマリRX(PRX=受信側デバイス)として構成されたnRF24L01は、6つの異なる受信用データパイプ(以下単にデータパイプ)を介し てデータを受信で きます。
データパイプには一意のアドレスがありますが、同じ周波数チャネルを共有します。
プライマリTX(PTX=送信側デバイス)として構成された最大6つの異なるnRF24L01は、PRXとして構成された1つのnRF24L01 と通信で き、PRXとして構成されたnRF24L01はそれらを区別できます。
データパイプ0には、一意の40ビットの構成可能なアドレスがあります。
データパイプ1〜5のそれぞれは8ビットの一意のアドレスを持ち、最上位アドレスビットの32ビットはデータパイプ1〜5で共有します。
すべてのデータパイプは、完全なEnhanced ShockBurst機能を実行できます。
nRF24L01は、受信パケットを確認するときにデータパイプアドレスを使用します。
これは、nRF24L01がペイロードを受信するのと同じアドレスでACKを送信することを意味します。
PTXデバイスでは、確認応答を受信するためにデータパイプ0が使用されるため、確認応答を受信できるようにするには、データパイプ0のアド レスが送信アドレスと等しくなければなりません。


自動パケットトランザクション処理は次のように機能します。

・PTXからPRXにデータパケットを送信して、トランザクションを開始します。
Enhanced ShockBurstは、ACKパケットを待つためにPTX(送信側デバイス)を受信モードに自動的に設定します。

・パケットがPRX(受信側デバイス)で受信されると、Enhanced ShockBurstは受信モードに戻る前に、自動的に確認パケット(ACKパケット)を組み立ててPTXに送信します。

・PTXがACKパケットをすぐに受信しない場合、Enhanced ShockBurstは元のデータパケットを自動的に再送信し、ACKパケットを待機するように受信モードにPTXを設定します。

これらの処理は、MCUの関与なしにモジュール側で自動的に行われます。



滅茶苦茶要約すると、Enhanced ShockBurstの機能とは、
1対6の通信ができて、送信側では受信側からのACKパケットを待って、受信側にデータが届いたかどうかを確認し、
ACKパケットが戻ってこないときは自動再送信を行う機能です。
送信側がACKパケットを待つ時間は250uS、再送信回数は3回ですが、これらはレジスターを書き換えることで変更可能です。
送信側のデータパイプ0(受信用ドレス0)は応答を受信するために使用されます。

これだけだとなんだかさっぱり分からないので整理してみました。
nRF24L01には送受信アドレスを格納する以下のレジスタが有ります。
アドレス 意味 長さ
0x10 送信用アドレス 40bit
0x0A 受信用アドレス0 40bit
0x0B 受信用アドレス1 40bit
0x0C 受信用アドレス2 8bit
0x0D 受信用アドレス3 8bit
0x0E 受信用アドレス4 8bit
0x0F 受信用アドレス5 8bit


受信側のレジスターを以下の様に設定します。
これで0xB1B1B1B1B1B1/0XC1C1C1C1C1/0xC1C1C1C1C2/0xC1C1C1C1C3 /0xC1C1C1C1C4/0xC1C1C1C1C5の6台からデータを受信することが出来るようになります。
アドレス 意味 長さ
0x10 送信用アドレス 40bit --
0x0A 受信用アドレス0 40bit 0xB1B1B1B1B1
0x0B 受信用アドレス1 40bit 0xC1C1C1C1C1
0x0C 受信用アドレス2 8bit 0xC2
0x0D 受信用アドレス3 8bit 0xC3
0x0E 受信用アドレス4 8bit 0xC4
0x0F 受信用アドレス5 8bit 0xC5

送信側のレジスターを以下の様に設定します。
アドレス 意味 長さ
0x10 送信用アドレス 40bit 0xB1B1B1B1B1
0x0A 受信用アドレス0 40bit 0xB1B1B1B1B1
0x0B 受信用アドレス1 40bit --
0x0C 受信用アドレス2 8bit --
0x0D 受信用アドレス3 8bit --
0x0E 受信用アドレス4 8bit --
0x0F 受信用アドレス5 8bit --


送信側が0xB1B1B1B1B1にデータを送信します。送信側は自動的にデータパイプ0(0xB1B1B1B1B1)からの応答待ちになりま す。
受信側が0xB1B1B1B1B1からデータを受信します。受信側は0xB1B1B1B1B1B1にACK応答を返します。
送信側が確認応答を受信できるようにするには、データパイプ0(受信用アドレス0)のアド レスが送信アドレスと等しくなければならない理由です。

送信側が2台の場合、2台目の送信側のレジスターを以下の様に設定します。
アドレス 意味 長さ
0x10 送信用アドレス 40bit 0xC1C1C1C1C1
0x0A 受信用アドレス0 40bit 0xC1C1C1C1C1
0x0B 受信用アドレス1 40bit --
0x0C 受信用アドレス2 8bit --
0x0D 受信用アドレス3 8bit --
0x0E 受信用アドレス4 8bit --
0x0F 受信用アドレス5 8bit --


送信側が0xC1C1C1C1C1にデータを送信します。送信側は自動的にデータパイプ0(0xC1C1C1C1C1)からの応答待ちになりま す。
受信側が0xC1C1C1C1C1からデータを受信します。受信側は0xC1C1C1C1C1にACK応答を返します。

SPI接続の無線モジュールはたくさん種類が有りますが、このように送信失敗を検出できるモジュールは非常に珍しいです。
CC1101、CC2500、nRF905、SI4432の無線モジュールを持っていますが、これらでは送信失敗を検出することができませんん。



nRF24L01とNRF24L01+の違いは1Cレジスターと1Dレジスターの有無です。
nRF24L01にはこれらのレジスターはありません。
これらのレジスターはDynamic PayloadとPayload with ACKのためのレジスターです。

nRF24L01はデフォルトでは固定長のデータをやり取りしますが、Dynamic Payloadを有効にすると、
32バイトまでの可変長のデータを扱うことが出来るようになります。
NULL値も有効なデータとして扱われるので、Dynamic Payloadを使わなくても、文字であれば
同じ動きとなります。

Payload with ACKを有効にすると、
単純なACKではなくPayload付き(32バイトまでのデータ付き)のACKを応答することができます。
但し、このPayload with ACKは受信する前に応答する値を設定しておく必要があり、
受信データに応じて応答値を変更することができないのが難点です。

nRF24L01もNRF24L01+も、2.4Ghz帯を使いますが、複数のチャネルを使うことができます。
ドキュメントによると、チャネル数のビット数は7ビットとなっているので、仕様上では0から127の128チャネル使えるようです。
試してみたらプログラム内で動的に使用するチャネルを変えることができますが、
2.4GHz+チャネル数の周波数を使うので、チャネル=0にすると2.4000GHz、チャネル=127にすると2.4127GHzとなり、
複数組のモジュールを同時に使うと、チャネルを変えても、ほとんどの場合混信します。

アプリケーションでnRF24L01とNRF24L01+を判定するのは結構面倒です。
1Cレジスターに適当な値を書き込んでから、1Cレジスターを読み込みます。
読み込んだ値が書き込んだ値と同じであればPlus、違っていれば無印となります。

続く...