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左右。

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

duye.chen

View Comments

  • 您好我想請問一下 能否用arduino測量出兩個nRF24L01的訊號強度?

  • 您好請問,要怎嚜設定db強度。我買的那個賣家有貼出

    默認模式輸出功率為2~3dBm, 如果要輸出7dBm,將暫存器位址0x06 (RF_SETUP) 暫存器的最低4位元寫入
    1(NRF24L01+沒有用到該位址,默認為0),即0x06寄存器最低四位元為1111即可。

    您知道要怎麼設定嗎?

  • 我在設定上遇到很大的問題

    在GettingStsrted裡傳輸正常
    但卻不太懂如何使用指令(包含自己寫程式)
    方便用信箱聯絡嗎?

  • 我現在的問題是TX端有訊號輸出
    可是RX端卻收不到 是我頻道位址沒設定好還是有其他地方我沒注意到
    就我現在的認為是可以從arduino裡寫程式修改頻道 是這樣可以嗎?

    • 據我所知 這個模組對電源有點要求,是否有上個電容濾波一下?

      我之前測試3.3v沒濾波就非常難收到訊號

      另是用getting started 程式測試嗎?你可已把兩個模組都先啟動發射,然後再把其中一個切回接收

Recent Posts

[教學] 打造你的 NFT 智能合約 – ERC721A

GM!前些日子在幣圈亂玩,一路...

2 年 ago

JavaScript – Singleton 設計模式

前言 在設計程式時,我們有時會...

3 年 ago

PlaidML 讓你的 Mac 也能加速 Tensorflow 機器學習!

相信很多使用 Mac 或者手上...

3 年 ago

RESTful API 測試很煩,只好動手寫屬於自己的測試了

寫在最前面 嗨,大家好久不見!...

3 年 ago

Node.js 與 Socket.io – 即時聊天室實作:資料庫

經過前兩篇(一、二)文章,我們...

6 年 ago