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板備用:

訊息接收端的nRF24L01範例程式

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

訊息接收端的nRF24L01範例程式

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

訊息接收端的setup函式

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

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

訊息接收端的read()函式

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

接收端的完整程式碼:

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

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

序列埠監控視窗

延伸閱讀

12 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

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *