Pages: [1]   Go Down
Author Topic: rf24HQ: a new library for the nRF24L01+ RF transceiver.  (Read 1341 times)
0 Members and 1 Guest are viewing this topic.
Wellington, New Zealand
Offline Offline
Sr. Member
****
Karma: 1
Posts: 404
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've released a library for the nRF24L01 here on github: https://github.com/harlequin-tech/rf24HQ. Its version 0.1, but it seems to be stable and I think its easier to use the Mirf and should be easy to extend.

It includes a Stream class (RFStream) that operates on top of the radio's packet interface. It lets you use the familiar available(), read(), and print() stream functions with the radio. It has example sketches for the streamer, and also for the scan function that lets you implement a basic RF scanner across the radio's 2.4GHz spectrum.

If you try it out and find problems, or its missing features, feel free to open an issue on the github project for support. I'll be working on it to add documentation for the functions, and to add features like full shock-burst support with 6 receive channels.

Here's the streamer example sketch (tested on an UNO):
Code:

/*
 * rfstream1.ino rf24HQ stream demo.
 *
 * Sends all characters received from the serial monitor to the partner radio.
 * Sends all characters received from the partner radio to the serial monitor.
 *
 * Run rfstream2.ino on the partner radio to connect to this sketch.
 *
 * Released to the public domain.
 *
 */

/*
 * The nrf24l01+ module should be connected to as follows:
 *
 *    rf24    atmega328
 *    ----    ---------
 *    GND  -> GND
 *    VCC  -> 3.3V
 *    CE   -> digital pin 8
 *    CSN  -> digital pin 9
 *    SCK  -> digital pin 13
 *    MOSI -> digital pin 11
 *    MISO -> digital pin 12
 */

#include <SPI.h>
#include <rf24HQ.h>

rf24 rf(8,9,100,RF24_MAX_SIZE);
RFStream rfstream;

const char myAddress[] = "rad01"; /* The rx address for this radio */
const char txAddress[] = "rad02"; /* Send data to this address */

void setup()
{
    Serial.begin(115200);
    rf.begin((Print *)&Serial);
    rfstream.begin(&rf, (Print *)&Serial);

    rf.setRxAddr(1, myAddress);
    rf.setTxAddr(txAddress);

    /* Turn on auto-ack, 1000 microsecond timeouts, 15 retries */
    rf.enableAck(1000, 15);

    Serial.print(myAddress);
    Serial.println(" ready");

    rfstream.print(myAddress);
    rfstream.println(F(" ready"));
}

void loop()
{
    if (rfstream.available()) {
        Serial.write(rfstream.read());
    }
    if (Serial.available()) {
        char ch = Serial.read();
        rfstream.write(ch);
Serial.write(ch);
    }
}
« Last Edit: April 22, 2012, 07:35:15 am by dhunt » Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12417
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


A quick look (not going to use the lib fro now)

Code:
void rf24::tx(const void *data, uint8_t len, uint8_t max)
{
  for (uint8_t ind=0; ind < len; ind++) {
    if (ind < max) {
      transfer(((uint8_t *)data)[ind]);
    } else {
      transfer(0);
    }
  }
}

can be simpler I guess . You don't want to send all those zero's or do I miss something?  (RX idem)
Code:
void rf24::tx(const void *data, uint8_t len, uint8_t max)
{
  if (len < max)
  {
    for (uint8_t ind=0; ind < len; ind++) transfer(((uint8_t *)data)[ind]);
  } else {
    transfer(0);
  }
}


functionname mismatches content ? as also an RX ADDR is set,...
Code:
void rf24::setTxAddr(const void *addr)
{
    writeReg(RX_ADDR_P0, (const uint8_t *)addr, RF24_ADDR_LEN);
    writeReg(TX_ADDR, (const uint8_t *)addr, RF24_ADDR_LEN);
}


this code is blocking, you might add a test function   boolean canSend();  so the user has the choice to test is it would block?
Code:
void rf24::send(void *data, uint8_t size)
{
    while (isSending(false));




Code:
    if (delay < 1) {
delay = 1;
    }
    delay = (delay + 249) / 250;
    if (delay > 15) {
delay = 15; /* Max 4000us */
    }

delay = constrain(delay,1,15);  // simpler ?


why not test the value of start too? > 125?
Code:
void rf24::scan(uint8_t *chans, uint8_t start, uint8_t count, uint8_t depth)
{
    uint8_t end = start+count;

    if (end > 125) {
end = 125;
    }

So some small remarks, in general it looks well done.

Thanks for sharing!


Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow!  I'd like to try this!  Looking forward to improvements and more testing...
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Wellington, New Zealand
Offline Offline
Sr. Member
****
Karma: 1
Posts: 404
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Rob,

thanks for taking a look.


A quick look (not going to use the lib fro now)

Code:
void rf24::tx(const void *data, uint8_t len, uint8_t max)
{
  for (uint8_t ind=0; ind < len; ind++) {
    if (ind < max) {
      transfer(((uint8_t *)data)[ind]);
    } else {
      transfer(0);
    }
  }
}

can be simpler I guess . You don't want to send all those zero's or do I miss something?  (RX idem)
Code:
void rf24::tx(const void *data, uint8_t len, uint8_t max)
{
  if (len < max)
  {
    for (uint8_t ind=0; ind < len; ind++) transfer(((uint8_t *)data)[ind]);
  } else {
    transfer(0);
  }
}

The function is intended to be used internally to write data into registers.  The case where it is used to transfer zeros is for the send() function where the user provides less data than the current packet length, so the tx() function fill out the rest of the tx buffer with zeros.

For rx(), yes I can probably not bother reading in zero's - I just haven't experimented with reading fewer bytes than the register holds yet.

Quote
functionname mismatches content ? as also an RX ADDR is set,...
Code:
void rf24::setTxAddr(const void *addr)
{
    writeReg(RX_ADDR_P0, (const uint8_t *)addr, RF24_ADDR_LEN);
    writeReg(TX_ADDR, (const uint8_t *)addr, RF24_ADDR_LEN);
}


Good point.  The RX_ADDR_P0 address is set for use with the auto-ack feature.  I'll update this so it only sets the RX address if auto-ack is enabled, and I'll have the enableAck() function copy the TX address to RX_ADDR_P0.

Quote
this code is blocking, you might add a test function   boolean canSend();  so the user has the choice to test is it would block?
Code:
void rf24::send(void *data, uint8_t size)
{
    while (isSending(false));

Yes it blocks. The user can call isSending() to see if the tx is busy - which I think is the same as canSend()?

I've considered ways to support all three TX buffers, but I haven't worked out a way to let the user work out if a transmit was successful in this case. Using only one TX buffer means gotAck() works correctly.

Quote

Code:
    if (delay < 1) {
delay = 1;
    }
    delay = (delay + 249) / 250;
    if (delay > 15) {
delay = 15; /* Max 4000us */
    }

delay = constrain(delay,1,15);  // simpler ?


why not test the value of start too? > 125?
Code:
void rf24::scan(uint8_t *chans, uint8_t start, uint8_t count, uint8_t depth)
{
    uint8_t end = start+count;

    if (end > 125) {
end = 125;
    }
Okay I can make this a bit safer :-).

Quote
So some small remarks, in general it looks well done.

Thanks for sharing!

Cheers!
Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 49
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi DHunt,

Just tried your rfscan example and it worked well, but am not sure of the results and how I use them,

At the moment I am using channel 90 with my Arduino and  nRF24L01+ module.

Where on the chart produced would channnel 90 be, would it be the 2492MHz result ?
Also is it best to be using the channel with the least Bars or the most Bars ?

And one final question how do you see your rf24HQ library developing in the future ?

Thanks for your great work !

Regards

Gary

Results:
2453MHz =   2 --
2454MHz =   2 --
2455MHz =   4 ----
2456MHz =   2 --
2457MHz =   2 --
2458MHz =   3 ---
2459MHz =   3 ---
2460MHz =   2 --
2461MHz =   3 ---
2462MHz =   4 ----
2463MHz =   2 --
2464MHz =   2 --
2465MHz =   3 ---
2466MHz =   3 ---
2467MHz =   2 --
2468MHz =   2 --
2469MHz =   4 ----
2470MHz =   2 --
2492MHz =   1 -
Logged

Wellington, New Zealand
Offline Offline
Sr. Member
****
Karma: 1
Posts: 404
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Quest,

The scanner shows an approximate measure of the signal strength at each frequency by estimating how busy each frequency is. It does this by checking how many times it sees a signal strength of -64dBm or higher when sampling the frequency 200 times.

The frequency is 2400MHz plus the channel, so channel 90 is 2490MHz.

If you have your second radio powered off or not transmitting, then the scan will show you which frequencies are in use in your environment. You should choose a clear frequency for the radio that is not being used, and at least a few channels away from any active channels.
Logged


Maryland, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 162
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Fancy!  I don't own any nRF24L01+'s yet but I have an order on the way (and I've been perusing the datasheet)...
Any functions to set the CRC length?  I might peruse the datasheet some more and see if anything else is missing.  Looks like a solid foundation for a nice library to me.

edit:
Looks like you'd need to keep track of the 1 vs 2-byte CRC status, and include that with the various setConfig() usages throughout the library (mainly enableRx and enableTx)...
« Last Edit: April 25, 2012, 10:32:18 am by spirilis » Logged

Dallas, Texas
Offline Offline
Sr. Member
****
Karma: 3
Posts: 267
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
rf24 rf(8,9,100,RF24_MAX_SIZE);

Appears to set the payload size to 32-bytes.

Code:
    /* Turn on auto-ack, 1000 microsecond timeouts, 15 retries */
    rf.enableAck(1000, 15);

I didn't look to see which data rate was in use, but this is potentially an issue or at least a gotcha for would-be users. If 250Kbps mode is selected, this can result in erroneous TX errors. In 250Kbps mode, the timeout should be 1500uS or greater given a payload size of 32-bytes. Given the comments of '0'-fill, I assume this means you're using fixed length payloads rather than dynamic payloads.

Now I'm assuming the data mode is NOT 250Kbps, but if anyone is attempting to get maximum range by simply changing their data rate via the demo, they'll definitely be led astray based on errant and possibly non-obvious results.

Given its a demo, you might considering adding comments to reference page 33 (section 7.4.2; Auto Retransmission) of the P-data sheet, and/or simply change the timeout to 1500; making it universally safe regardless of desired data rate.
Logged


Melbourne
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Hmmmm
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Hi Thought I would test out this library as it seems more complete then MIRF and could allow more fine tune control over the radio. 

So I tested this on some of my nodes and noticed that RX/TX ADDR fields are all out of wack.

If I flash over my existing sketch that uses MIRF and has a RX ADDR of "Nodes" with this library without powering down the radio I can run rf.dumpRegisters and see RX_ADDR_P0 = "Nodes"


if I then
    rf.setTxAddr("Nodes");
    rf.setRxAddr(1, "Nodes");
    rf.setRxAddr(2, "Nodes");
    rf.setRxAddr(3, "sedoN");

rf.dumpRegisters shows
RX_ADDR_P0 = "Nodes"
RX_ADDR_P1 = "sedoN"
RX_ADDR_P2 = "s"
RX_ADDR_P3 = "N"
TX_ADDR = "sedoN"

so it seems to show that the TX/RX addresses are reversed and for RX ADDR >P1 are only stored as a single CHAR

if I power completely power down and back up the radio then P0 and P2-P5 defaults to the below 
RX_ADDR_P0  (0A): E7 E7 E7 E7 E7  .....
RX_ADDR_P5  (0F): C6  .

Logged

Melbourne
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Hmmmm
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Dhunt,

I am unable to get your code working sending/receiving data.
I can get the RFscan working but not much luck with anything else I have this loaded onto 4 nodes and none are receiving data. I currently use MIRF without issue on the same hardware OK.

For the code I use pin10 instead of pin8 for CE and use the same ADDR for both RX&TX

This is the code so far;

Code:
//RFnode rf24HQ test sketch RFnode_RF24HQ_test -GR0B 15/05/12

#include <SPI.h>
#include <rf24HQ.h>

const int led_red_pin = 4;           
const int led_green_pin = 5;
uint8_t CE_Pin =10;
uint8_t CSN_Pin =9;
uint8_t RF_channel = 1;
uint8_t RF_packetSize = 16; // max = 32


rf24 rf(CE_Pin,CSN_Pin,RF_channel,RF_packetSize);          //  (CE_Pin,  CSN_pin,  RF_channel,  PacketSize<32)


//Led_R blink
void Led_R(){
  digitalWrite(led_red_pin, HIGH);                                           
  delay(1);           
  digitalWrite(led_red_pin, LOW);   
}

//Led_G blink
void Led_G(){                               
  digitalWrite(led_green_pin, HIGH);                                         
  delay(1);           
  digitalWrite(led_green_pin, LOW);   
}


void setup(){
  Serial.begin(9600);
  Serial.println("Booting RFnode");
  pinMode(led_red_pin, OUTPUT);           
  pinMode(led_green_pin, OUTPUT);   
  Led_R();
  Led_G();

    rf.begin((Print *)&Serial);           //start wireless
    rf.setTxAddr("Nodes");                                //this seems to get reveresed 
    rf.setRxAddr(1, "Nodes");                             //0 is ok, 1 is reversed, 2-5 single char
    rf.setSpeed(RF24_SPEED_MAX);                          //RF24_SPEED_MIN=2=250Kbps  0=1Mbps  RF24_SPEED_MAX=1=2Mbps   note: need to double check this
    rf.dumpRegisters();
    Serial.println("Wireless initialized.");
}

void loop(){
  Led_R();
  Led_G();
//  rf.dumpRegisters();


//send a packet
     char data[16] = "Hello world";
     rf.send(data, sizeof(data));
     //Debug.Dump whole sent packet as HEX
     Serial.print(">>Sent: ");                                           
     for (int i = 0; i < 16; i = i + 1) {
        Serial.print(data[i],HEX);
        Serial.print(",");
     }
     Serial.println();


//receive packets
     while (rf.available()) {
rf.read(data, sizeof(data));
         //Debug.Dump whole received packet as HEX
         Serial.print("<<Got: ");                                           
         for (int i = 0; i < 16; i = i + 1) {
           Serial.print(data[i],HEX);
           Serial.print(",");
         }
         Serial.println(); 
     }
delay(1000);
}
Logged

Pages: [1]   Go Up
Jump to: