之前我們製作了一個關於ESP8266-01的說明教材(ESP8266安裝指南),該模組是一種小尺寸WiFi模組,可以讓使用者在專案中輕鬆添加WiFi功能。今天我們將討論 nRF24L01+RF模組,該模組是ESP8266 ESP-01的姐妹模組,能夠讓使用者在專案中添加無線射頻通信功能。nRF24L01+和ESP8266 ESP-01具有類似的外形和引腳佈局(從遠處看完全一樣),但是受控方式和功能完全不同。在本教材中,我們將會介紹此RF模組的使用基礎,還會說明該模組如何與其他RF模組和微控制器通信。為此,我們將展示該模組如何與Arduino Uno微控制器相連接。
nRF24L01+模組是以北歐半導體公司的nRF24L01+“2.4GHz ISM(工業、科學和醫療)頻段RF收發IC”為基礎。
2.4GHz ISM頻段工作
3.額定Vcc為3.3V(可承受5V輸入)
片上穩壓
無線傳輸速率為250kbps, 1 Mbps, 2Mbps
超低功耗工作
低電流消耗(900nA – 26μA)
6條資料管道
首先,我們會介紹使用該模組的硬體部分。與ESP-01類似,該RF模組配備4×2公頭介面。然而,其實際引腳排列與ESP-01模組不同,因為該模組通過不同的通信協定(SPI)與其他設備進行通信。如果您想瞭解有關SPI協定的更多資訊,請查看我們的 Arduino通信協議教材!!
該RF模組的引腳佈局如下圖所示(來自 Addicore網站)。
該RF模組作為SPI的從動裝置來進行使用,因此只能與具有專用SPI通信線路的元件一起工作。這意味著圖中的SPI MOSI、MISO和SCK(時鐘)引腳必須連至微控制器的相應引腳。Arduino上的對應引腳如下:
CE和CSN引腳可以連接Arduino上的任何輸出GPIO引腳。進行SPI通信初始化時,軟體應適當指定這些引腳。
RF模組和Arduino之間的連接示例如下所示:
為了讓Arduino與該模組對接,我們將使用 TMRh20的RF24程式,該程式將RF模組和MCU之間的低層通信封裝成了易於使用的C++類。
深入研究該模組的使用之前,我們首先來介紹一些相關的基礎知識。在美國,射頻設備僅能使用FCC分配的頻率範圍。ISM頻段是FCC為科學和醫療儀器預留的通信頻段,我們的RF模組將通過ISM頻段內的頻率進行通信。該RF模組的使用並不需要知道這些頻率的細節或者這些頻率的通信究竟如何發生。我們將專注於可以控制的無線RF通信的不同方面。
如果您流覽RF24程式檔案,您就會發現很多參數可以做設置。一些關鍵參數列舉如下:
F24程式檔案提供了許多非常好的範例代碼,以便於我們一窺其門徑。範例專案的連結位址如下: http://tmrh20.github.io/RF24/examples.html
在下文中,我們將會看到上述參數被初始化成為“入門”Arduino代碼。代碼連結如下:
http://tmrh20.github.io/RF24/GettingStarted_8ino-example.html
GettingStarted.ino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
[code]/* * Getting Started example sketch for nRF24L01+ radios * This is a very basic example of how to send data from one node to another * Updated: Dec 2014 by TMRh20 */ #include #include "RF24.h" /****************** User Config ***************************/ /*** Set this radio as radio number 0 or 1 ***/ bool radioNumber = 0; /* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */ RF24 radio(7,8); /**********************************************************/ byte addresses[][6] = {"1Node","2Node"}; // Used to control whether this node is sending or receiving bool role = 0; void setup() { Serial.begin(115200); Serial.println(F("RF24/examples/GettingStarted")); Serial.println(F("*** PRESS 'T' to begin transmitting to the other node")); radio.begin(); // Set the PA Level low to prevent power supply related issues since this is a // getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default. radio.setPALevel(RF24_PA_LOW); // Open a writing and reading pipe on each radio, with opposite addresses if(radioNumber){ radio.openWritingPipe(addresses[1]); radio.openReadingPipe(1,addresses[0]); }else{ radio.openWritingPipe(addresses[0]); radio.openReadingPipe(1,addresses[1]); } // Start the radio listening for data radio.startListening(); } void loop() { /****************** Ping Out Role ***************************/ if (role == 1) { radio.stopListening(); // First, stop listening so we can talk. Serial.println(F("Now sending")); unsigned long start_time = micros(); // Take the time, and send it. This will block until complete if (!radio.write( &start_time, sizeof(unsigned long) )){ Serial.println(F("failed")); } radio.startListening(); // Now, continue listening unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds boolean timeout = false; // Set up a variable to indicate if a response was received or not while ( ! radio.available() ){ // While nothing is received if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop timeout = true; break; } } if ( timeout ){ // Describe the results Serial.println(F("Failed, response timed out.")); }else{ unsigned long got_time; // Grab the response, compare, and send to debugging spew radio.read( &got_time, sizeof(unsigned long) ); unsigned long end_time = micros(); // Spew it Serial.print(F("Sent ")); Serial.print(start_time); Serial.print(F(", Got response ")); Serial.print(got_time); Serial.print(F(", Round-trip delay ")); Serial.print(end_time-start_time); Serial.println(F(" microseconds")); } // Try again 1s later delay(1000); } /****************** Pong Back Role ***************************/ if ( role == 0 ) { unsigned long got_time; if( radio.available()){ // Variable for the received timestamp while (radio.available()) { // While there is data ready radio.read( &got_time, sizeof(unsigned long) ); // Get the payload } radio.stopListening(); // First, stop listening so we can talk radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. radio.startListening(); // Now, resume listening so we catch the next packets. Serial.print(F("Sent response ")); Serial.println(got_time); } } /****************** Change Roles via Serial Commands ***************************/ if ( Serial.available() ) { char c = toupper(Serial.read()); if ( c == 'T' && role == 0 ){ Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK")); role = 1; // Become the primary transmitter (ping out) }else if ( c == 'R' && role == 1 ){ Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK")); role = 0; // Become the primary receiver (pong back) radio.startListening(); } } } // Loop[/code] |
檔案頂部的前兩行是二個C++ #include指令:第一指令包含Arduino SPI程式(如前所述,RF模組使用SPI與Arduino進行通信);第二條包含RF24程式。
#include <SPI.h>
#include “RF24.h”
接下來的這行指令構建了RF24對象行:RF24 radio(7,8); 傳遞給構造函數的這兩個參數是連至射頻模組的CE和CSN數位引腳。MOSI、MISO和SCK引腳必須分別是引腳11、12和13,而CE和CSN可以是任意兩個數位引腳!
接下來我們看一下讀寫的管道地址。您可能已經猜到,對於相互通信的兩個無線設備來說,寫入管道位址和讀取管道位址正好互換,因為其中一個無線設備的寫入管道是另一個設備的讀取管道。無線設備的位址可以是24位、32位或40位。在範例代碼中,這些位址由C++字串轉換而來,但是,您也可以用二進位或十六進位格式指定。比如,一個40位的位址如果指定為十六進位,那麼可能為0xF0F0F0F0F0。作為良好的程式設計實踐,存儲寫入管道位址和讀取管道位址時,應在一個陣列中放置兩個值。在示例代碼中,寫入管道位址和讀取管道位址存儲在名為“位址”的位元組陣列中。
在void setup()方法中,我們需要提供初始化無線設備的位址管道參數以及其他參數的指令。
首先,我們要調用方法RF24::begin()。針對radio物件調用其它RF24程式方法之前,我們必須調用begin()方法,因為該方法初始化RF晶片。
N接下來,調用RF24::setPALevel()方法,初始化功放(PA)等級。RF24程式提供多個不同常數值,用來指定功放等級。較高的PA等級意味著模組的通信距離更長,但是在運行過程中消耗的電流更大。對於入門使用來說,我們將RF_24_LOW常數作為一個參數傳遞給setPALevel()方法,因為兩個通信模組之間的距離不會很大。一般來說,當Arduino板與RF模組相配合時,應保持PA等級盡可能地低,以減小Arduino穩壓電源的電流。
接下來,我們看一下如何初始化讀寫管道。我們已經將寫入管道和讀取管道定義為位元組值。現在,我們必須將這些定義傳遞給radio物件,從而讓它瞭解寫入管道位址和讀取管道位址。寫入管道通過openWritingPipe()方法設置,而讀取管道則通過openReadingPipe()方法設置。打開寫入管道和讀取管道的範例如下:
radio.openWritingPipe(addresses[1]);
radio.openReadingPipe(1, addresses[0]);
請注意,必須為openReadingPipe()方法傳遞一個額外的整數,以指明初始化哪個讀取管道。這是因為該RF模組在給定時間內可以打開多達6個讀取管道!
範例代碼通過一個 role布林值適當分配讀取管道和寫入管道值。根據 role的值,程式會確定RF模組是ping設備還是 pong設備。您的項目也可以使用類似代碼。必須確保兩個設備上的讀寫位址互換,否則系統不會傳輸或讀取資料!
調用 RF24::startListening() 方法之後,無線模組開始進行偵聽。重要須知:指示射頻模組開始偵聽資料之前,必須初始化讀取管道(即調用 openReadingPipe()方法之前必須調用startListening() 方法!)
類似地,RF24類還提供stopListening()方法,無線模組開始寫入之前必須調用該方法
在範例代碼中,您可能會注意到,系統指示無線模組使用RF24::available()方法檢查是否存在傳入資料。這類似於我們之前見過的Serial::available()和SoftwareSerial::available() 方法,如果RF連接存在可用資料,那麼available()方法會返回真(true),表示可以讀取資料。
最後,RF24類提供了真正寫入和讀取資料的方法。用於RF24::write()和RF24::read()方法的參數如下:(1)一個指標,指向與所發送資料類型相同的變數;以及(2)被發送資料的長度。在 read() 方法中,指標指向的變數接收正在讀取的資料。在write()方法中,指標指向的變數會保存正在寫入的資料。在這兩個方法中,絕對有必要確保指標指向與要發送資料類型相同的變數,並且傳遞給方法的長度實際上反映了資料大小。向read()和write()方法傳遞不正確的類型或長度可能導致非期望的資料截斷,從而導致傳輸資料無效。在“入門”代碼中,需要傳輸的資料是 無符號長整數
無符號長整數(unsigned long)。因此,作為參數傳遞給read()和write()方法的指標指向一個類型為無符號長整型的變數。還有一點必須清楚,傳輸資料的長度始終是無符號長整數的長度。在這種情況下,長度不需要明確作為一個整數傳遞,該長度參數可以簡單地寫為sizeof(unsigned long)。
“入門”代碼唯一沒有涉及的參數就是通信通道。如果需要特定通道(比如您擁有多個RF網路,但是您不希望它們相互干擾),那麼可以向RF24::setChannel()方法傳遞一個8位元整數參數,從而設定通道。示例代碼如下:
radio.setChannel(10);
嘗試將二個RF模組連至二個獨立的Arduino板,並分別載入“入門”代碼(對於其中一個板,您必須將role 的布林值改為1)。您現在應該可以通過相應的ping時間發送資訊並接收返回資訊!下圖是兩張並排的“ping”和“pong”序列監視器(serial monitors)截圖:
恭喜您完成了nRF24L01+教材的學習!您現在具備了使用這些優秀射頻模組打造自己專案的技能和知識!您還可以查看Device Plus部落格,來瞭解更多使用這些RF模組的專案!