Arduino與樹莓派間的通信實踐

最近需要在Arduino之間,以及Arduino和上位機(樹莓派)之間傳輸數據,
原有APC220設備雖然可用,使用也方便,但成本太高,不容易批量,遂尋求其他方案。


一、方案選擇

根據搜索的結果和前人經驗,有如下幾種可行方案:

  • nRF24L01+ (RF)
  • ESP8266(WIFI)
  • XBee (ZigBee)
  • ENC28J60(LAN)
  • W5100,W5500(LAN)

其中,Xbee爲最優選,但成本太高。
LAN方案不方便,WIFI方案功耗高,藍牙方案傳輸距離短,
故考慮RF方案,成本和效果平衡較好。
nRF24L01+價格便宜(5塊左右,做工好帶天線的15左右),編程簡單,
且存在能同時支持樹莓派、Arduino、Linux的RF24庫。
Git連接爲:https://github.com/TMRh20/RF24.git

便宜的(做工一般的)nRF24L01開發板:
這裏寫圖片描述


二、接線

1、nRF24L01+引腳圖
nRF24L01+引腳圖
- 1:地
- 2:3.3V(切不可接5V,燒片)
- 3:CE(RF讀寫控制引腳)
- 4:CSN(選片引腳)
- 5:SCK(SPI時鐘)
- 6:MOSI(SPI主出從入)
- 7:MISO(SPI主入從出)
- 8:IRQ(外部中斷)

2、接線方法

編號 nRF24L01 Arduino Mega Arduino UNO Rpi(物理管腳)
1 GND 9
2 VCC 1
3 CE 7(可自定義) 7(可自定義) 15(GPIO22)
4 CSN 8(可自定義) 8(可自定義) 24(SPI0CS1)
5 SCK 52 13 23
6 MOSI 51 11 19
7 MISO 53 12 21
8 IRQ - - -

接線示意圖:
- Arduino UNO
這裏寫圖片描述

- Arduino Mega
- 這裏寫圖片描述
-
- RaspberryPi3
這裏寫圖片描述


三、代碼&運行

RF24庫中自帶的GettingStarted例子非常方便,其代碼包含發送端和接收端兩種類型,
默認爲接受模式,輸入T時切換爲發送,輸入R則切爲接受模式,並有簡單的超時判斷。
爲了易於理解,可簡單修正代碼,讓接收端返回一自增數字。

1、 Arduino
1) RF24庫安裝
https://github.com/TMRh20/RF24.git下載RF24後,
將其複製到Arduino安裝目錄下的libraries目錄下,啓動ArduinoIDE後,從例子中選擇RF24->GettingStarted。

2) 代碼修改
發送端不必修改,直接編譯上傳即可。(注意UNO和Mega的選擇和串口選擇)
接收端將代碼中的radioNumber從默認的0修改爲1。如下:

bool radioNumber = 0; (自身爲2Node,發送給1Node)
  ->
bool radioNumber = 1;(自身爲1Node,發送給2Node)

簡言之,1Node爲接收端,2Node爲發送端。
建議:原代碼中的got_time不易觀察理解,
可將接收端中的got_time發送前賦值爲一靜態可增計數值。

3) 運行
發送端啓動後,輸入T,使其進入發送模式。
接受端啓動即可,無需輸入R。(默認爲R接收模式)
如上述配置接線正常,可在Serial Monitor中看到發送方和接收方的輸出,大致如下:
發送方圖(靜態自增變量):
這裏寫圖片描述

2、 樹莓派(RaspberryPi)
本文中使用的樹莓派爲 16年新發布的RPi3 B型,其管腳如下:
這裏寫圖片描述

1) RF庫安裝
將RF24庫複製到樹莓派(或通過git直接獲取)。進入RF24目錄後執行如下命令,進行編譯和安裝(選擇SPI方式)

./configure --driver=SPIDEV
sudo make install -B

2) 修改系統配置

修改/etc/modprobe.d/raspi-blacklist.conf,如果其中存在
blacklist spi-bcm2708,將其註釋。

修改/etc/modules文件,在其中追加一行,開啓SPI。
spidev

reboot重啓樹莓派後,/dev下會新增spidev0.0和spidev0.1兩個設備文件。

3) 代碼修改
修改RF24/example_linux/GettingStarted.cpp文件,
同上面的Arduino一樣,發送端不必修改,
接收端將radioNumber從默認的0修改爲1,並建議吧回送的時戳數據改爲自增數字。
在當前目錄下執行make後,生成GettingStarted的二進制文件。

4) 運行
使用sudo ./ GettingStarted執行,並輸入0進入接收模式。
如Arduino的發送端配置、運行正常,則正常發送迴應包。大致如下(自增變量版):
這裏寫圖片描述


四、注意&體會

便宜版本的nRF24L01效果一般,很容易受到干擾。帶天線的會好些,真做項目不可圖便宜。
接線要準確,SPI要理解下原理。CE、CSN其實是可以任意指定的,只是要修改下RF24的初始化代碼。


五、RH24例子代碼簡單說明

以下是RH24(TMRh20)自帶的Arduino例子,簡單說明一下,
樹莓派上爲C語言實現的版本,變量、語法略有區別,但邏輯是基本一致的。

  • 變量定義
bool radioNumber = 1;  // RF節點名稱決定Flag

/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
// 指定CE用GPIO7,CSN用GPIO8,需要和接線一致
// 如接線不採用7,8,代碼這裏需要修改。
RF24 radio(7, 8); 

byte addresses[][6] = {"1Node", "2Node"}; // 兩個節點名

bool role = 0; // 發送&接收模式Flag
  • 初始化函數
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();
  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]);
  }

  // 默認爲監聽模式,開始監聽
  radio.startListening();
}
  • 執行邏輯
void loop() {

/****************** Ping Out Role ***************************/  
  if (role == 1)  { // 發送模式

    radio.stopListening();  // 發送數據前要停止監聽
    Serial.println(F("Now sending"));

    unsigned long start_time = micros(); // 待發送的時戳

    // RF24內部會自動處理payload和發送數據不等長的問題
    if (!radio.write( &start_time, sizeof(unsigned long) )){ // 發送數據
       Serial.println(F("failed"));
     }

    radio.startListening(); // 數據發送完,需要監聽迴應數據的到來

    unsigned long started_waiting_at = micros(); 
    boolean timeout = false; 

    while ( ! radio.available() ){  // 超時判斷
      if (micros() - started_waiting_at > 200000 ){ 
          timeout = true;
          break;
      }      
    }

    if ( timeout ){  
        Serial.println(F("Failed, response timed out."));

    }else{ // 讀數據並顯示數據和間隔時間
        unsigned long got_time;  
        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 )  { // 接收模式
    static long count = 1;  // 自增計數
    unsigned long got_time = 0;

    if( radio.available()){

      while (radio.available()) { // 讀數據
        radio.read( &got_time, sizeof(unsigned long) ); 
      }

      got_time = count++;  // 爲便於理解,回送自增計數值
      radio.stopListening(); 
      radio.write( &got_time, sizeof(unsigned long) ); // 寫回應
      radio.startListening(); 
      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;  

   }else
    if ( c == 'R' && role == 1 ){  //接受模式
      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));      
       role = 0; 
       radio.startListening();    
    }
  }

} // Loop

五、參考URL

詳細參考(英語):
http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo

其他人的成功經驗:
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3053467.html
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3048315.html
注意:其所使用的RF24庫並不相同[^footnote]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章