miércoles, 14 de mayo de 2014

Connecting a NRF24 board to Arduino (TO BE FINISHED)


When we want to develop wireless electronic applications, the NRF24 chip comes in handy. It is a low power 2.4GHz transceiver chip with lots of interesting features. And on top of that it is really cheap, you can get a couple of ready to use boards for just around 1€!

The chip pinout has several lines dedicated to power supply, oscillator, antenna, etc, but I am assuming we are going to use the mentioned ready to use board. This board usually has the following pinout:


Now let's see what each line is for:
  • GND: This is the gound line. No comment xD
  • VCC: Power supply line. It is worth mentioning the NRF24 chip admits from 1.9v to 3.6v, but the SPI interface is 5v tolerant, so we can connect it to arduinos running powered at 5v but we have to take care to not power the NRF24 chip with more than 3.6v.
  • CE: Chip enable. This line activates the transceiver.
  • CSN: This is the SPI chip select line.
  • SCK: This is the SPI clock line.
  • MOSI: This is the SPI Master Out Slave In line.
  • MISO: This is the SPI Master In Slave Out line.
  • IRQ: The NRF24 chip is capable of generating interrupts using this line.
With that said, it should be pretty straight forward to connect this board to an Arduino. In my case I am using an Arduino Pro Mini (MHz, 3.3v) atmega328p. The final wire diagram is as follows:

NRF24 board pin     Arduino board pnin
1                               gnd
2                               vcc
3                               10
4                               4
5                               13
6                               11
7                               12
8                               not used

My setup looks like that:



At this point we have covered the hardware part, now let's go with software. The easiest way to play with NRF24 is by using a library, this way we can be sure it has been tested and is fullly functional, furthermore we don't have to go into further details. We are going to use an awesome library from maniacbug that you can find here: maniacbug's Arduino library.

To use the library just follow the instructions, then start the Arduino IDE and you should find a new tab called NRF24 under File->Examples. Select GettingStarted as is illustrated in the next picture:


This example is almost the same as the next one:
/*
 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.
 */

/**
 * Example for Getting Started with nRF24L01+ radios. 
 *
 * This is an example of how to use the RF24 class.  Write this sketch to two 
 * different nodes.  Put one of the nodes into 'transmit' mode by connecting 
 * with the serial monitor and sending a 'T'.  The ping node sends the current 
 * time to the pong node, which responds by sending the value back.  The ping 
 * node can then see how long the whole cycle took.
 */

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"

//
// Hardware configuration
//

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 

RF24 radio(10, 4); //SN: set CE and CSN pins

//
// Topology
//

// Radio pipe addresses for the 2 nodes to communicate.
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

//
// Role management
//
// Set up role.  This sketch uses the same software for all the nodes
// in this system.  Doing so greatly simplifies testing.  
//

// The various roles supported by this sketch
typedef enum { role_ping_out = 1, role_pong_back } role_e;

// The debug-friendly names of those roles
const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};

// The role of the current running sketch
role_e role = role_pong_back;

void setup(void)
{
  //
  // Print preamble
  //

  Serial.begin(57600);
  printf_begin();
  printf("\n\rRF24/examples/GettingStarted/\n\r");
  printf("ROLE: %s\n\r",role_friendly_name[role]);
  printf("*** PRESS 'T' to begin transmitting to the other node\n\r");

  //
  // Setup and configure rf radio
  //

  radio.begin();

  // optionally, increase the delay between retries & # of retries
  radio.setRetries(15,15);

  // optionally, reduce the payload size.  seems to
  // improve reliability
  //radio.setPayloadSize(8);

  //
  // Open pipes to other nodes for communication
  //

  // This simple sketch opens two pipes for these two nodes to communicate
  // back and forth.
  // Open 'our' pipe for writing
  // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)

  //if ( role == role_ping_out )
  {
    //radio.openWritingPipe(pipes[0]);
radio.openWritingPipe(pipes[1]);
radio.openReadingPipe(1,pipes[0]);

//    radio.openReadingPipe(1,pipes[1]);
  }
  //else
  {
    //radio.openWritingPipe(pipes[1]);
    //radio.openReadingPipe(1,pipes[0]);
  }

  //
  // Start listening
  //

  radio.startListening();

  //
  // Dump the configuration of the rf unit for debugging
  //

  radio.printDetails();
}

void loop(void)
{
  //
  // Ping out role.  Repeatedly send the current time
  //

  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
    unsigned long time = millis();
    printf("Now sending %lu...",time);
    bool ok = radio.write( &time, sizeof(unsigned long) );
    
    if (ok)
      printf("ok...");
    else
      printf("failed.\n\r");

    // Now, continue listening
    radio.startListening();

    // Wait here until we get a response, or timeout (250ms)
    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\r");
    }
    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\r",got_time,millis()-got_time);
    }

    // Try again 1s later
    delay(1000);
  }

  //
  // Pong back role.  Receive each packet, dump it out, and send it back
  //

  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;
      bool done = false;
      while (!done)
      {
        // Fetch the payload, and see if this was the last one.
        done = radio.read( &got_time, sizeof(unsigned long) );

        // Spew it 
//        printf("Got payload %lu...",got_time);

 // Delay just a little bit to let the other unit
 // make the transition to receiver
 delay(20);
      }

      // First, stop listening so we can talk
      radio.stopListening();

      // Send the final one back.
      radio.write( &got_time, sizeof(unsigned long) );
//      printf("Sent response.\n\r");

      // Now, resume listening so we catch the next packets.
      radio.startListening();
    }
  }

  //
  // Change roles
  //

  if ( Serial.available() )
  {
    char c = toupper(Serial.read());
    if ( c == 'T' && role == role_pong_back )
    {
      printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r");

      // Become the primary transmitter (ping out)
      role = role_ping_out;
      radio.openWritingPipe(pipes[0]);
      radio.openReadingPipe(1,pipes[1]);
    }
    else if ( c == 'R' && role == role_ping_out )
    {
      printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r");
      
      // Become the primary receiver (pong back)
      role = role_pong_back;
      radio.openWritingPipe(pipes[1]);
      radio.openReadingPipe(1,pipes[0]);
    }
  }
}

Now we connect our first Arduino to the computer and program it, then we repeat the operation with the second Arduino and we are ready to test everything.

Now you can connect both Arduinos, and don't forget to set one the these as transmitter by pressing T.

If everything went right, you should get something similar to the following for your emitter node:
RF24/examples/GettingStarted/                                                                     
ROLE: Pong back                                                                                   
*** PRESS 'T' to begin transmitting to the other node                                             
STATUS           = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0                              
RX_ADDR_P0-1     = 0xf0f0f0f0d2 0xf0f0f0f0e1                                                      
RX_ADDR_P2-5     = 0xc3 0xc4 0xc5 0xc6                                                            
TX_ADDR          = 0xf0f0f0f0d2                                                                   
RX_PW_P0-6       = 0x20 0x20 0x00 0x00 0x00 0x00                                                  
EN_AA            = 0x3f                                                                           
EN_RXADDR        = 0x03                                                                           
RF_CH            = 0x4c                                                                           
RF_SETUP         = 0x07                                                                           
CONFIG           = 0x0f                                                                           
DYNPD/FEATURE    = 0x00 0x00                                                                      
Data Rate        = 1MBPS                                                                          
Model            = nRF24L01+                                                                      
CRC Length       = 16 bits                                                                        
PA Power         = PA_HIGH                                                                        
*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK                                        
Now sending 3823...ok...Got response 3823, round-trip delay: 25                                   
Now sending 4849...ok...Got response 4849, round-trip delay: 25                                   
Now sending 5875...ok...Got response 5875, round-trip delay: 25                                   
Now sending 6901...ok...Got response 6901, round-trip delay: 25                                   
Now sending 7927...ok...Got response 7927, round-trip delay: 23                                   
Now sending 8951...ok...Got response 8951, round-trip delay: 25                                   
Now sending 9977...ok...Got response 9977, round-trip delay: 25                                   
Now sending 11003...ok...Got response 11003, round-trip delay: 25                                 
Now sending 12029...ok...Got response 12029, round-trip delay: 25                                 
Now sending 13056...ok...Got response 13056, round-trip delay: 24                                 
Now sending 14082...ok...Got response 14082, round-trip delay: 24                                 
Now sending 15108...ok...Got response 15108, round-trip delay: 24                                 
Now sending 16134...ok...Got response 16134, round-trip delay: 24                                 
Now sending 17160...ok...Got response 17160, round-trip delay: 22                                 
Now sending 18184...ok...Got response 18184, round-trip delay: 24                                 
Now sending 19210...ok...Got response 19210, round-trip delay: 24                                 
Now sending 20236...ok...Got response 20236, round-trip delay: 24

As for future applications we are developing a PCB to connect both Arduino and NRF24 boards, besides some general use space and a temperature and LDR sensor. The following picture shows this prototype:



Connecting a NRF24 board to a Raspberry Pi (TO BE FINISHED)


In this brief article we will learn how to connect the NRF24l01 wireless sensor to the Raspberry Pi (Rpi) PIO using bare cables. In my case I bought a couple of NRF24l01 ready to use boards from eBay at about 1€.

The first thing we should know to accomplish this is what kind of bus the NRF24l01 chip has. In fact, as we can see in the datasheet, it uses the SPI bus which is fully compatible with the Raspberry Pi, although not out of the box. We will cover the wire diagram first, in the following pictures we can see both Rpi and NRF24l01 board pinouts:

The NRF24 CE and CSN can be a bit confusing. The former is not the SPI CE but a global enable line for the whole chip. The latter is indeed the SPI CE line.
As we can see in the Rpi pinout (right picture), the SPI lines are purple. We also need an additional IO line for the NRF24 CE (chip enable) line, we will use GPIO25. So the wire diagram is as follows:

NRF24 board pin                 Rpi pin
1                                           3v3
2                                           Ground
3                                           GPIO25
4                                           GPIO8(CE0)
5                                           GPIO11(SCLK)
6                                           GPIO10(MOSI)
7                                           GPIO9(MISO)
8                                           not used

It is important to know the NRF24l01 supply voltage range is [1.9-3.6]V but it is 5V tolerant input. Also, I am using a Rpi GPIO adapter board for simplicity which I also bought from eBay (Raspberry Pi GPIO kit extension board adapter breadboard 26pin GPIO ribbon cable). So my final setup is as follows:



Once we have successfully connected both boards we can have a look at software. As I said above, usually Rpi linux distributions don't have the SPI support enabled by default, so we have to solve it loading the proper module. In my case executing the following command from a shell is enough:
$ sudo modprobe spi-bcm2708
From this point on a pair of SPI device control files should have been created under /dev directory. Let's check that:
$ ls /dev/spidev*
/dev/spidev0.0  /dev/spidev0.1
Finally we have to download a library and a test program. We will use stanleyseow's library, which is a port of the excellent maniacbug's Arduino library for NRF24. To copy and compile it just do the following:
$ git clone https://github.com/stanleyseow/RF24
$ cd RF24/librf24-rpi
$ make
$ sudo make install
Now we need some code to test the board. The following simple piece of code is taken from the example named GettingStarted found in maniacbugs's library :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
#include "RF24.h"

using namespace std;
RF24 radio("/dev/spidev0.0",8000000 , 25);  //spi device, speed and CSN,only CSN is NEEDED in RPI
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

void setup(void)
{
  // Print preamble
  printf("\n\rRF24/examples/GettingStarted/\n\r");

  // Setup and configure rf radio
  radio.begin();

  // optionally, increase the delay between retries
  radio.setRetries(15,15);

  // optionally, reduce the payload size.  seems to improve reliability
  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1,pipes[0]);

  // Start listening
  radio.startListening();

  // Dump the configuration of the rf unit for debugging
  radio.printDetails();
}

void loop(void)
{
// First, stop listening so we can talk.
radio.stopListening();

// Take the time, and send it.  This will block until complete
unsigned long time = __millis();
printf("Now sending %lu...",time);
bool ok = radio.write( &time, sizeof(unsigned long) );

if (ok)
printf("ok...");
else
printf("failed.\n\r");

// Now, continue listening
radio.startListening();

// Wait here until we get a response, or timeout (250ms)
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\r");
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\r",got_time,__millis()-got_time);
}

// Try again 1s later
sleep(1);
}

int main()
{
setup();
while(1)
loop();

return 0;
}

To compile it just execute:
$ g++ -o test nrf24_test.cpp -L../RF24/librf24-rpi/librf24  -lrf24 -I../RF24/librf24-rpi/librf24
As we can see, this test is very simple, it just initializes the nrf24 chip an start receiving data and sending it back as an acknowledge. From the Arduino serial console, If we press T(transmit) it starts transmitting and waiting for the acknowledge, if we press R(receive) it goes back to reception.

Since it is a communication test we obviously need an emitter and a receiver. You can use two Rpi, but instead I am using and Arduino (another step by step guide for that) with the same exact piece of code (obviously not ported to Rpi). Next picture shows what I mean:



Finally if everything is working fine we should see something like that for the emitter and receiver:

Emitter:
$ sudo ./test

RF24/examples/GettingStarted/
SPI device  = /dev/spidev0.0
SPI speed  = 8000000
CE GPIO  = 25
STATUS  = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1  = 0xf0f0f0f0d2 0xf0f0f0f0e1
RX_ADDR_P2-5  = 0xc3 0xc4 0xc5 0xc6
TX_ADDR  = 0xf0f0f0f0d2
RX_PW_P0-6  = 0x20 0x20 0x00 0x00 0x00 0x00
EN_AA  = 0x3f
EN_RXADDR  = 0x03
RF_CH  = 0x4c
RF_SETUP  = 0x07
CONFIG  = 0x0f
DYNPD/FEATURE  = 0x00 0x00
Data Rate  = 1MBPS
Model  = nRF24L01+
CRC Length  = 16 bits
PA Power  = PA_MAX
Now sending 4192465834...ok...Got response 4192465834, round-trip delay: 30
Now sending 4192466867...ok...Got response 4192466867, round-trip delay: 30
Now sending 4192467900...ok...Got response 4192467900, round-trip delay: 30
Now sending 4192468933...ok...Got response 4192468933, round-trip delay: 30
Now sending 4192469966...ok...Got response 4192469966, round-trip delay: 30
Now sending 4192470999...ok...Got response 4192470999, round-trip delay: 30
Now sending 4192472032...ok...Got response 4192472032, round-trip delay: 30
Now sending 4192473065...ok...Got response 4192473065, round-trip delay: 30
Now sending 4192474098...ok...Got response 4192474098, round-trip delay: 30
Now sending 4192475131...ok...Got response 4192475131, round-trip delay: 30

Receiver:
*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK                                      
Got payload 4192465834...Sent response.                                                        
Got payload 4192466867...Sent response.                                                        
Got payload 4192467900...Sent response.                                                        
Got payload 4192468933...Sent response.                                                        
Got payload 4192469966...Sent response.                                                        
Got payload 4192470999...Sent response.                                                        
Got payload 4192472032...Sent response.                                                        
Got payload 4192473065...Sent response.                                                        
Got payload 4192474098...Sent response.                                                        
Got payload 4192475131...Sent response.


Additional resources:
http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
http://hack.lenotta.com/arduino-raspberry-pi-switching-light-with-nrf24l01/
http://maniacbug.wordpress.com/2011/11/02/getting-started-rf24/