RF24 專屬於 nRF24L01 的程式函式庫。

我在大學研究的專題可以說是自幹一個 Zigbee 的無線網路系統,我所採用無線傳輸模組是不在 IEEE 規範中的 2.4GHz 無線網路模組 nRF24L01,並且利用 Arduino 以及 Raspberry Pi 做雛形開發。很久之前還有為了它與 Launchpad 之間的通訊而寫過一篇文章。至於我專題的細節,改天再來說說。

nRF24L01 是一個非常便宜,便宜到可以說是不可思議的無線傳輸模組,且透過 SPI 的方式與微處理器溝通與傳輸資料。以下是 nRF24L01 的一些特色。

  • 使用2.4GHz全球開放頻寬
  • 126個可選擇頻道
  • 可設定收發位址及頻率
  • 最高 2Mbps 的資料傳輸速率
  • 可程式控制的輸出功率(最大0dBm,消耗11.3mA)
  • 1.9~3.3V低電壓
  • 使用SPI界面控制
  • 細節可以參考官方網站

如果你剛剛有去翻閱一下先前寫的那篇文章的話,應該會對於這個傳輸模組有些微的概念。但那篇文章的操作環境主要是在 Launchpad 這個由 TI 出品的開發板上,而今天要介紹的,則是讓你能透過 Raspberry Pi 的 GPIO 來操作這個無線傳輸模組。

RF24

RF24 是一個支援多種單板電腦的 C++ 程式函式庫(Library),因為這種外接模組並不像是 USB 裝置那樣,可以隨插隨用,而是要透過一些特殊的程式才能運作,而通常這些程式都是由一些義工愛好者們所提供的。RF24便是其中之一。

RF24 提供許多方便好用的 function 讓你可以快速的掌握 nRF24L01 ,並且讓他可以跟 Raspberry Pi 緊密結合,做更多你想用他做的事情。

目前支援的單板電腦除了 Raspberry 以外,還有…

  • Intel Galileo
  • Arduino Uno, Nano… ATMega 328 核心的
  • Arduino Mega Boards
  • Arduino Due
  • ATTiny
  • 其他細節可以參考他的 Git Page

RF24 Github:https://github.com/maniacbug/RF24

Class Reference

這部分可以直接參考官方的內容,因為項目繁多,我就不一一介紹了。

範例

範例程式其實包在所下載下來的資料包中,不過都是英文,但我想我把 GettingStarted.cpp 這個範例給簡單的翻譯一下,也順便讓你了解這個函式庫的使用方法。

GettingStarted.cpp

這是一個 Raspberry Pi 的範例,執行時需要兩個 nRF24L01 的裝置才能運作,一個可以是 Raspberry Pi 另一個是 Arduino。Arduino 的範例程式你可以在這邊看到。

/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 03/17/2013 : Charles-Henri Hallard (http://hallard.me)
              Modified to use with Arduipi board http://hallard.me/arduipi
                          Changed to use modified bcm2835 and RF24 library
TMRh20 2014 - Updated to work with optimized RF24 Arduino library
 */
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <RF24/RF24.h>
using namespace std;
//
// Hardware configuration
// 硬體參數設定
// Configure the appropriate pins for your connections
// 設定要接到 nRF24L01 指定的腳位參數
// Radio CE Pin, CSN Pin, SPI Speed
// 無線模組的 CE、CSN 以及 SPI 傳輸速度
// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz
//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ);
// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz
//RF24 radio(RPI_V2_GPIO_P1_22, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ);
// NEW: Setup for RPi B+
//RF24 radio(RPI_BPLUS_GPIO_J8_15,RPI_BPLUS_GPIO_J8_24, BCM2835_SPI_SPEED_8MHZ);
//
// Setup for GPIO 15 CE and CE0 CSN with SPI Speed @ 8Mhz
RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);
/********** User Config *********/
// Assign a unique identifier for this node, 0 or 1
bool radioNumber = 1;
/********************************/
// Radio pipe addresses for the 2 nodes to communicate.
// 設定兩個無線模組節點的位址,用來傳輸訊息用
const uint8_t pipes[][12] = {"1Node","2Node"};
int main(int argc, char** argv){
  bool role_ping_out = true, role_pong_back = false;
  bool role = role_pong_back; // 初始設定為 pong
  printf("RF24/examples/GettingStarted/\n");
  // Setup and configure rf radio
  // 設定與初始化無線模組的參數
  radio.begin();
  // optionally, increase the delay between retries & # of retries
  // 不一定需要,這是設定獲取資訊的延遲時間設定
  radio.setRetries(15,15);
  // Dump the configuration of the rf unit for debugging
  // 輸出無線模組的參數方便 debug
  radio.printDetails();
/********* Role chooser ***********/
  printf("\n ************ Role Setup ***********\n");
  string input = "";
  char myChar = {0};
  // 輸入 0 為 pong , 1 為 ping
  cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit) \n>";
  getline(cin,input);
  if(input.length() == 1) {
    myChar = input[0];
    if(myChar == '0'){
        cout << "Role: Pong Back, awaiting transmission " << endl << endl;
    }else{  cout << "Role: Ping Out, starting transmission " << endl << endl;
        role = role_ping_out;
    }
  }
/***********************************/
  // This simple sketch opens two pipes for these two nodes to communicate
  // back and forth.
  // 這個簡單的範例會開兩個管線給兩個節點,方便訊息的傳送
    if ( !radioNumber )    {
      radio.openWritingPipe(pipes[0]);
      radio.openReadingPipe(1,pipes[1]);
    } else {
      radio.openWritingPipe(pipes[1]);
      radio.openReadingPipe(1,pipes[0]);
    }

    radio.startListening();

    // forever loop
    // 無窮迴圈
    while (1)
    {
        if (role == role_ping_out)
        {
            // First, stop listening so we can talk.
            // 首先,先停止監聽,這樣才能發送訊息
            radio.stopListening();
            // Take the time, and send it.  This will block until complete
            // 獲取現在時間,然後傳送出去。這部分會運作到寄送完成後才會結束。
            printf("Now sending...\n");
            unsigned long time = millis();
            bool ok = radio.write( &time, sizeof(unsigned long) );
            if (!ok){
                printf("failed.\n");
            }
            // Now, continue listening
            // 好,現在回到監聽狀態
            radio.startListening();
            // Wait here until we get a response, or timeout (250ms)
            // 等待到對方回應,或者超過 250 毫秒的等待時間
            unsigned long started_waiting_at = millis();
            bool timeout = false;
            while ( ! radio.available() && ! timeout ) {
                if (millis() - started_waiting_at > 200 )
                    timeout = true;
            }
            // Describe the results
            if ( timeout )
            {
                printf("Failed, response timed out.\n");
            }
            else
            {
                // Grab the response, compare, and send to debugging spew
                // 接取回應,比較時間差,然後印出除錯資訊
                unsigned long got_time;
                radio.read( &got_time, sizeof(unsigned long) );
                // Spew it
                printf("Got response %lu, round-trip delay: %lu\n",got_time,millis()-got_time);
            }
            sleep(1);
        }
        //
        // Pong back role.  Receive each packet, dump it out, and send it back
        // Pong back 做的事情跟 ping out 差不多。差別在,這邊是收完封包並印出後,再把把它丟還給來源端
        //
        if ( role == role_pong_back )
        {

            // if there is data ready
            // 如果資料已經可以收進來處理時
            // 後續動作與前者相似
            if ( radio.available() )
            {
                // Dump the payloads until we've gotten everything
                unsigned long got_time;
                // Fetch the payload, and see if this was the last one.
                while(radio.available()){
                    radio.read( &got_time, sizeof(unsigned long) );
                }
                radio.stopListening();

                radio.write( &got_time, sizeof(unsigned long) );
                // Now, resume listening so we catch the next packets.
                radio.startListening();
                // Spew it
                printf("Got payload(%d) %lu...\n",sizeof(unsigned long), got_time);

                delay(925); //Delay after payload responded to, minimize RPi CPU time

            }

        }
    } // forever loop
  return 0;
}

總結

其實個人覺得這個無線模組不難用,但問題在於傳輸距離。雖然宣稱可以長達 100 公尺以上的傳輸距離,但那只限定在沒有過多障礙物且傳輸速率較慢的環境下才有可能抵達這麼遠的距離,在我的實測過程中,2Mbps的速率下,最多只能到30M左右。

但是他的便宜、小巧以及低功耗可以讓它在小區域的無線控制上佔上一席之地。

獨夜:

看評論 (8)