nRF24L01無線收發器模組與Arduino通訊實驗(二):一對一通訊

本單元將使用兩個nRF24L01,以及兩片Arduino Uno控制板建立一對一無線通訊實驗。從這個簡單的例子,可以認識nRF24L01程式庫的設定頻道、通道、功率和收發訊息的指令,以及新、舊程式庫的一些參數設置的不同點。

nRF24L01一對一無線通訊實驗

實驗材料

  • Arduino Uno控制板 × 2
  • nRF24L01無線收發模組 × 2

實驗電路

接線範例如下,SPI有固定的接腳,CE和CSN可接任何腳;由於程式庫的範例程式將CE和CSN分別接在7和8腳,所以許多人就這麼沿用下來了:

nRF24L01實驗電路

傳送端和接收端的兩個控制板的接線方式都一樣。

訊息傳送端的nRF24L01範例程式

nRF24L01的Arduino程式庫第一版由maniacbug開發(以下稱為舊版),TMRh20在這個基礎上做了一些改進,推出第二版(以下稱為新版),本文的範例採用新版程式庫,請讀者先到RF24程式庫專案網頁下載、安裝。

程式開頭要引用SPI和RF24這兩個程式庫,接著建立RF24類別物件,筆者將此物件命名為rf24

傳送端程式設置

發射端要設定一個通道5個字元長度的位址,筆者將它命名為“1Node”,詳細的位址格式說明請參閱下文。

接下來,在setup()函式裡面執行一些nRF24L01模組的初始化工作。這些工作項目以及RF24程式庫提供的指令名稱對照如下:

傳送端setup()

主程式迴圈loop(),負責每隔一秒傳送一次訊息。傳送訊息的指令是write(),每一則訊息最大長度為32位元組。

nRF24L01程式庫write()指令

每一次傳送的訊息最長不超過32位元組,因此,若傳送如下的字串,超出32位元組的部份將被截斷:

The quick brown fox jumps over the lazy dog.

這是因為nRF24L01的訊息封包(每次傳送資料的基本單位)當中,資料(payload)長度最大為32位元組。底下是SB(突發)模式的訊息封包格式:

nRF24L01的訊息封包

底下將說明本文使用到的指令,完整的指令列表和語法說明,請參閱RF24新版的API文件

設定nRF24L01的廣播功率和資料傳輸速率

設定廣播功率的setPALevel(),需要輸入底下代表功率的任一常數名稱:

  • RF24_PA_MIN(最小功率)
  • RF24_PA_LOW(低功率)
  • RF24_PA_HIGH(高功率)
  • RF24_PA_MAX(最大功率)

這些功率值和晶片本身的消耗電流分別為:-18dBm∕7.0mA(最低功率)、-12dBm∕7.5mA(低功率)、-6dBM∕9.0mA(高功率)以及0dBm∕11.3mA(最大功率)。如果沒有執行setPALevel(),RF24程式庫預設將採用RF24_PA_MAX(最大功率)。

設定資料傳輸速率的setDataRate(),需要設定底下代表傳輸速率的任一常數:

  • RF24_250KBPS:250kbs
  • RF24_1MBPS:1Mbps
  • RF24_2MBPS:2Mbps

請注意,nRF24晶片有nRF2401和nRF2401+兩種型號,只有“+”號的,才具有250kbs傳輸率設定。若不確定,請只選用1Mbps和2Mbps。如果沒有執行setDataRate(),RF24程式庫預設將採用RF24_2MBPS(最高速)。

通道位址設定說明

新版RF24程式庫中,通道(pipe)的位址格式為5個字元,而且不同通道位址的第一個字元不能相同,因此第一個字元通常用數字:

通道(pipe)的位址格式

通道的位址長度可以是3~5位元組。下圖的通道位址格式說明,取自Nordic公司的nRF24L01技術文件2.0版第36頁。

這張圖指出,通道0和1的位址是完整長度,而通道2~5的位址只有1位元組,它們的高位元部份,都沿用通道1的設置,因此,這1個位元組必須是唯一值。

設定位址時,新版RF程式庫將取用設定名稱中的第一個字元,作為通道2~5的識別位址值。除了使用字串,也可以用16進制數字設定位址:

字串和16進制數字設定位址

底下的表格取自技術文件第55頁,從「位元」欄位也能看出,通道0~1的位址資料長度為40位元(5位元組),通道2~5的位址長度為8位元(1位元組):

位址暫存器長度

通道位址數字一般都寫成16進制格式,例如:0xF0E0D0C0CC。在舊版RF24程式庫中,設定通道位址的參數採用數字格式。Arduino的長整數(long類型)為32位元長度,儲存40位元長度的數字,必須使用64位元長整數類型,程式寫法如下:

64位元長度類型

我們可以自行設定位址數字,但根據Nordic官網的How to choose an address?(如何選擇位址)這篇文章指出,為了降低資料封包的出錯率,位址數字應避免用0x00, 0x55, 0xAA和0xFF開頭

頻道設定說明

上一篇文章提到nRF24L01晶片可設定0~125頻道,在晶片內部,實際負責設定頻道值的暫存器叫做RF_CH,設定頻道的setChannel()指令,就是把參數寫入RF_CH暫存器。nRF24無線頻率的計算方式,是用2400MHz加上RF_CH的值:

通訊頻率計算公式

例如,若RF_CH的值為83,無線電頻率就是2483MHz。請注意,超過83以後的頻率,超出ISM的2.4GHz免許可證頻帶(2.4-2.4835 GHz),若發射功率過高,可能會觸法(註:各國對於免許可證的頻帶定義不一);但另一方面,選擇超過ISM的頻率可以避開擁擠的2.4GHz頻段。

RF_CH具有7個有效位元,可表達的值介於0~127(2的7次方是128);因此,舊版的RF24程式庫的頻道設定指令可接受值0~127,但晶片的實際運作頻帶範圍是2.4GHz~2.525GHz,所以新版程式庫將頻道的最高值設成125。

完整的nRF24L01模組發射端程式碼

完整的發射端程式碼如下,請上傳到一個Arduino板備用:

#include <SPI.h>
#include "RF24.h"

RF24 rf24(7, 8); // CE腳, CSN腳

const byte addr[] = "1Node";
const char msg[] = "Happy Hacking!";

void setup() {
  rf24.begin();
  rf24.setChannel(83);       // 設定頻道編號
  rf24.openWritingPipe(addr); // 設定通道位址
  rf24.setPALevel(RF24_PA_MIN);   // 設定廣播功率
  rf24.setDataRate(RF24_250KBPS); // 設定傳輸速率
  rf24.stopListening();       // 停止偵聽;設定成發射模式
}

void loop() {
  rf24.write(&msg, sizeof(msg));  // 傳送資料
  delay(1000);
}

訊息接收端的nRF24L01範例程式

接收端的程式碼開頭和發射端類似,同樣要設定通道位址,另外多了一個通道編號

訊息接收端的nRF24L01範例程式

在setup()函式設定頻道、功率和速率之後,透過openReadingPipe()設定接收訊息的通道位址,最後執行startLisening()開始監聽無線電訊號

訊息接收端的setup函式

openReadingPipe()裡的「通道編號」的可能值介於0~5,因為只有通道0和1是完整位址,設定一對一通訊時,建議使用通道1,而通道0在ESB(增強型突發協定)中,預設當作「接收端傳回收到訊息的回應」之用。

接收端的loop()函式結構,跟處理UART序列埠(就是USB序列埠)通訊程式類似,不停地詢問nRF24L01模組:「有資料進來了嗎?」,如果有,就進行處理:

訊息接收端的read()函式

available()的選擇性「通道編號」是指標類型,如果要指定的話,請記得在通道變數名稱前面加上“&”符號

接收端的完整程式碼:

#include <SPI.h>
#include "RF24.h"

RF24 rf24(7, 8); // CE腳, CSN腳

const byte addr[] = "1Node";
const byte pipe = 1;  // 指定通道編號

void setup() {
  Serial.begin(9600);
  rf24.begin();
  rf24.setChannel(83);  // 設定頻道編號
  rf24.setPALevel(RF24_PA_MIN);
  rf24.setDataRate(RF24_250KBPS);
  rf24.openReadingPipe(pipe, addr);  // 開啟通道和位址
  rf24.startListening();  // 開始監聽無線廣播
  Serial.println("nRF24L01 ready!");
}

void loop() {
  if (rf24.available(&pipe)) {
    char msg[32] = "";
    rf24.read(&msg, sizeof(msg));
    Serial.println(msg); // 顯示訊息內容 
  }
}

請把程式碼上傳到另一個連接好nRF24L01模組的Arduino板,這個收訊端的板子要接電腦的USB,傳送端的板子可以只接電源,不必接在電腦上。

開啟序列埠監控視窗,將能看到發送端傳入的訊息:

序列埠監控視窗

延伸閱讀

Posts created 467

42 thoughts on “nRF24L01無線收發器模組與Arduino通訊實驗(二):一對一通訊

  1. 之前在使用nRF24L01模組的時候,只是將範例修改成符合自己的需求來使用而已。看完了這二篇圖文並茂的文章,對這個模組有更深一層的認識了,非常感謝~~~

    1. 我在測試的時候也發生過類似的情況(收到一堆無關的數字),後來發現可能是電源干擾,我換用一條Arduino和電腦的USB連接線,問題就解決了。

      thanks,
      jeffrey

  2. 接线图和代码备注中,CSN和CE引脚均标注反了,按图接线的话会有一大堆乱码,折腾了好一会才试着看了看文档。
    原文是RF24 rf24(7, 8); // CSN腳, CE腳
    实际上RF24库自己的表格里,明确写着在UNO默认,CE在Pin7,CSN在Pin8
    PS:万能的某宝上,单价2.95的模块,各种环境反复测试了很久也木有干扰哎,难道是偶运气好?

    1. 刚刚看了NRF24L01+驱动的类文档,我的确把CSN和CE脚搞反了,本周我会重新测试NRF24L01并且更新文章,非常感谢您的纠错!

      【9/12更新】我已經更新了電路接線圖與程式註解。但也許是手邊的NRF24L01被我玩掛了,測試結果仍是亂碼。

      thanks,
      jeffrey

  3. 完全复制您简化的发射端和接收端代码,亲测通讯完全正常。用的还是3块钱的山寨芯片模块。看来您手头的是CE和CSN接反太久挂了。
    补货的话,最近无线模块淘宝价格战打的厉害,正品nRF24L01P+PA+LNA还带外置天线,亲测市内1.5km通信稳定,拿样单价5.9元人民币还包邮,限两块,SX1278也差不多价,妥妥亏本,不知他们发不发台湾,哈哈。

    1. 感谢告知!不过,除了电源之外,nRF24L01P+的I/O脚都容许5V输入,因此CE和CSN反接并不会对它有任何影响。

      thanks,
      jeffrey

  4. 您好請問如過是Arduino Uno 發射 , Arduino MEGA2560 R3 接收
    接法以及寫法都是一樣?

    1. Arduino Mega2560的SPI接腳和Uno板不同,這是Mega2560的SPI腳:

      MOSI: 51
      MISO: 50
      SCK: 52
      SS: 53

      thanks,
      jeffrey

  5. 老師您好
    您文章中的這段話
    available()的選擇性「通道編號」是指標類型,如果要指定的話,請記得在通道變數名稱前面加上“&”符號。
    我在接收端寫上

    void loop() {
    if (radio.available(&pipe)) {

    驗證時卻會出現
    exit status 1
    invalid conversion from ‘const byte* {aka const unsigned char*}’ to ‘uint8_t* {aka unsigned char*}’ [-fpermissive]

    當我把&pipe刪掉
    驗證時卻又正常了
    請問這是發生什麼問題呢?

  6. 您好,我有購買2塊NRF24L01+ PA LNA,然後依照您的方法來使用,我單晶片是使用arduino nano,並焊在萬用板上,原本使用正常,並且成功發射接收,但不知為何,突然完全無法運作,執行程式會卡在 rf24.write(&msg, sizeof(msg)); 這行,請問有辦法解決嗎?

  7. 老師您好,

    我使用單向的一對一通訊,一端發,另一端收,沒問題。
    但是我要做slave 端接收到master端的命令後才回送資料給master,master 收到slave的資料後,才再向slave發送傳送命令的請求。
    初始化完後
    1: master 傳送給slave (然後master 進入listen 狀態),slave 收到後,再傳送資料發射給master。
    2: slave 這邊的 回傳值 ok = rf24.write() 顯示是 true.
    3: 但master 這邊是一直處在 rf24.available() 的等待狀態。
    4: 我用第三個接收器,去確認salve 是否成功發送資料出來,第三個接收器的確有收到slave 發送的資料。
    5: 先前master 與 slave 是只用一個1Node。我把master 與slave間的node 改成2個{“1Node”,”2Node”},master 發送給slave用”2Node”, slave 回傳給master 用”1Node”。但結果還是同上面1~4, master始終是在發送請求資料後,就一直無法收到slave回傳的資料。
    6. 我都有按照發送時,先設rf24.stopListening(),要接收前先設rf24.startListening().
    7. 結論:master 這塊板子在單獨測試 tx & rx 都沒問題,但改為tx 再 rx ,就有問題。請問,這是什麼原因? 謝謝。

    1. 欸…我好幾個月沒碰nRF24L01了,也許改天會再拿出來玩玩~先麻煩你仔細閱讀原廠技術文件。

      thanks,
      jeffrey

  8. 您好老師請問如果要寫成if else是這樣嗎?
    if (rf24.available(&pipe)==data1)
    {
    rf24.read(&data1, sizeof(data1));
    digitalWrite(LEDPin1,HIGH);
    Serial.println(data1);
    }
    else(rf24.available(&pipe)==data2)
    {
    rf24.read(&data2, sizeof(data2));
    digitalWrite(LEDPin1,LOW);
    Serial.println(data2);
    }

    我想要讀到data1 LED亮,如果讀到讀到data2 LED不亮。

  9. 你好,真的很實用,可以問一下要怎麼把要寫的訊息改成很多個(要傳的訊息有四種),要怎麼讀取?
    非常感謝

  10. 老師,請問下載完檔案並解壓縮後,要把哪項程式庫移至Arduino 的libraries裡呢,或要下載什麼?

    1. 你是指RF24程式庫嗎?下載後把解壓縮的資料夾移入「文件」的Arduino/libraries路徑即可,相關說明請參閱9-16頁。

      thanks,
      jeffrey

    2. 其實不用這麼麻煩,第二代Library的作者有將RF24的Library放到Arduino IDE的”管理程式庫”功能中,直接搜尋安裝就可以了。

  11. 謝謝老師精心的講解與操作,讓我這個初學者能夠有機會學習。
    但是我一直無法解決的問題是
    發送端在序列埠視窗顯示皆為正常
    但是接收端的部分本來應該要讀取到的字元,卻是空白和括號形同亂碼,如下
    ⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮)P

    這實在令我費解,望老師您有空時能夠回覆我的疑問,謝謝。

  12. 老師你好

    我依照你的範例,接收端編譯後卻跳出

    C:\Users\Administrator\Desktop\led_slave\led_slave.ino: In function ‘void loop()’:

    C:\Users\Administrator\Desktop\led_slave\led_slave.ino:21:27: warning: invalid conversion from ‘const byte* {aka const unsigned char*}’ to ‘uint8_t* {aka unsigned char*}’ [-fpermissive]

    if (rf24.available(&pipe)) {

    ^

    In file included from C:\Users\Administrator\Desktop\led_slave\led_slave.ino:2:0:

    C:\Program Files (x86)\Arduino\libraries\RF24-master/RF24.h:329:8: note: initializing argument 1 of ‘bool RF24::available(uint8_t*)’

    bool available(uint8_t* pipe_num);

    序列埠完全沒收到發送過來的文字,想問老師有遇過這情況嗎?

  13. 請問要如何用nRF24L01發送可變電阻的類比訊號控制接收端arduino的設備

  14. 感恩老師的教學
    我是初學者,有用433k模組傳輸。在接收端也有4位7段顯示器模組計數。
    發覺到不相容,兩個不能在一起。
    想請問老師
    用這個無線
    可以接七段顯示器嗎?

    1. 我猜想你所謂的「不相容」指的是「訊息格式」不一樣,你可能需要自行把433k模組收到的資料,轉換成七段顯示器模組的輸入資料格式。

  15. 老師 請問可以透過2.4 ghz的方式 達成無線上傳草稿碼給arduino開發板嗎?

  16. 您好:我想請問我用nRF24L01+的模組測試是ok的,但換成nRF24L01+PA+LNA之後就無法使用了,而且還有把nRF24L01+PA+LNA用另外的電源供電(12V1A),並且降壓成3.3V獨立供電給它使用,不知是否程式碼有需要更動?

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top