ADVERTISEMENT
Making your sensors wireless is always a convenient solution to avoid cabling hassle. The available, cheap 2.4ghz transceiver are a good solution. They have a small wireless chip which makes two way communications between two arduinos or any other micro-controllers much easier. They are available in different form factors and the commonly available ones are shown in the figure. When you get the parts it is always thrilling to test it out in the shortest possible time. After searching for nrf24l01 arduino tutorials, i ended up with a lot of information's and a huge chunk of codes with a lot of wrong information/misguidance for beginners. So i planned to write one short and quick start guide for using nrf24l01 module with arduino uno and a working sketch to test.
So in short the radio module needs to be connected to arduino board as follows
Now let us have a look at the pin connections for commonly available nrf24l01 module. See the picture where two most commonly seen variants are shown. One of them has 10 pins (2 grounds (GND) and two vcc (3v3)
Now let us make two module speaking to each other. This is from the rf24 examples folder. Make two sets where the first one sends the signal, second the second one receives and send it back to first and the time taken for the loop is shown. When i tried the examples, i didn't set the order of which module to transmit and which one to receive and i was wondering why it was not working! So to make simple i made two sketches which are 99.9% similar except in the rad and write variables. Upload SKETCH1 to the first one which is kept connected to a computer and keep arduino serial monitor open. The second one is on an arduino which is kept far from the computer and can be powered with a 9volt battery. The second one receives the signal and relays it back to the first one and the response can be viewed on a PC. So to make it simple, load sketch2 to an arduino with nrf24l module and then keep it away from pc with a 9v battery attached. Then load sketch1 to the next arduino and open its serial monitor to see the response.
SKETCH2 (for the arduino which will send pback the response, battery powered)
SKETCH1 (for the arduino connected to the computer with serial monitor and this keeps transmitting to the arduino with SKETCH2 loaded and listen for the reply)
So in short the radio module needs to be connected to arduino board as follows
nRF24L01 | Arduino PIN |
GND | GND |
3V3 (VCC) | 3V3 (IMPORTANT!!) |
CE | 9 |
CSN | 10 |
SCK | 13 |
MOSI | 11 |
MISO | 12 |
Now let us have a look at the pin connections for commonly available nrf24l01 module. See the picture where two most commonly seen variants are shown. One of them has 10 pins (2 grounds (GND) and two vcc (3v3)
Now install RF24 library which makes things easy. Download it from here To install this, unzip the files and rename the folder to RF24 (from RF24-master) and place this folder in your arduino libraries folder (mine was at Desktop/arduino-1.0.5/libraries/) and restart the arduino ide. Now the library is ready to go. There are examples and i was unable to understand them at the first time. So am giving some sketch (basically from the same source) which you can upload and test without thinking too much.
Wireless scanner
----------------------
It just need only one module to test. It acts like a poormans scanner (from Mark) and shows the signals around. Paste this script in arduino ide and upload it to the uno board. Open the serial monitor by pressing the small magnifier button on the ide and set baud rate to 57600 (else you will see some gibberish text)
#include <SPI.h>
// Poor Man's Wireless 2.4GHz Scanner
//
// uses an nRF24L01p connected to an Arduino
//
// Cables are:
// SS CSN) -> 10
// MOSI -> 11
// MISO -> 12
// SCK -> 13
//
// and CE -> 9
//
// created March 2011 by Rolf Henkel
//
#define CE 9
// Array to hold Channel data
#define CHANNELS 64
int channel[CHANNELS];
// greyscale mapping
int line;
char grey[] = " .:-=+*aRW";
// nRF24L01P registers we need
#define _NRF24_CONFIG 0x00
#define _NRF24_EN_AA 0x01
#define _NRF24_RF_CH 0x05
#define _NRF24_RF_SETUP 0x06
#define _NRF24_RPD 0x09
// get the value of a nRF24L01p register
byte getRegister(byte r)
{
byte c;
PORTB &=~_BV(2);
c = SPI.transfer(r&0x1F);
c = SPI.transfer(0);
PORTB |= _BV(2);
return(c);
}
// set the value of a nRF24L01p register
void setRegister(byte r, byte v)
{
PORTB &=~_BV(2);
SPI.transfer((r&0x1F)|0x20);
SPI.transfer(v);
PORTB |= _BV(2);
}
// power up the nRF24L01p chip
void powerUp(void)
{
setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x02);
delayMicroseconds(130);
}
// switch nRF24L01p off
void powerDown(void)
{
setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)&~0x02);
}
// enable RX
void enable(void)
{
PORTB |= _BV(1);
}
// disable RX
void disable(void)
{
PORTB &=~_BV(1);
}
// setup RX-Mode of nRF24L01p
void setRX(void)
{
setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x01);
enable();
// this is slightly shorter than
// the recommended delay of 130 usec
// - but it works for me and speeds things up a little...
delayMicroseconds(100);
}
// scanning all channels in the 2.4GHz band
void scanChannels(void)
{
disable();
for( int j=0 ; j<200 ; j++)
{
for( int i=0 ; i<CHANNELS ; i++)
{
// select a new channel
setRegister(_NRF24_RF_CH,(128*i)/CHANNELS);
// switch on RX
setRX();
// wait enough for RX-things to settle
delayMicroseconds(40);
// this is actually the point where the RPD-flag
// is set, when CE goes low
disable();
// read out RPD flag; set to 1 if
// received power > -64dBm
if( getRegister(_NRF24_RPD)>0 ) channel[i]++;
}
}
}
// outputs channel data as a simple grey map
void outputChannels(void)
{
int norm = 0;
// find the maximal count in channel array
for( int i=0 ; i<CHANNELS ; i++)
if( channel[i]>norm ) norm = channel[i];
// now output the data
Serial.print('|');
for( int i=0 ; i<CHANNELS ; i++)
{
int pos;
// calculate grey value position
if( norm!=0 ) pos = (channel[i]*10)/norm;
else pos = 0;
// boost low values
if( pos==0 && channel[i]>0 ) pos++;
// clamp large values
if( pos>9 ) pos = 9;
// print it out
Serial.print(grey[pos]);
channel[i] = 0;
}
// indicate overall power
Serial.print("| ");
Serial.println(norm);
}
// give a visual reference between WLAN-channels and displayed data
void printChannels(void)
{
// output approximate positions of WLAN-channels
Serial.println("> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <");
}
void setup()
{
Serial.begin(57600);
Serial.println("Starting Poor Man's Wireless 2.4GHz Scanner ...");
Serial.println();
// Channel Layout
// 0 1 2 3 4 5 6
// 0123456789012345678901234567890123456789012345678901234567890123
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//
Serial.println("Channel Layout");
printChannels();
// Setup SPI
SPI.begin();
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setBitOrder(MSBFIRST);
// Activate Chip Enable
pinMode(CE,OUTPUT);
disable();
// now start receiver
powerUp();
// switch off Shockburst
setRegister(_NRF24_EN_AA,0x0);
// make sure RF-section is set properly
// - just write default value...
setRegister(_NRF24_RF_SETUP,0x0F);
// reset line counter
line = 0;
}
void loop()
{
// do the scan
scanChannels();
// output the result
outputChannels();
// output WLAN-channel reference every 12th line
if( line++>12 )
{
printChannels();
line = 0;
}
}
Now let us make two module speaking to each other. This is from the rf24 examples folder. Make two sets where the first one sends the signal, second the second one receives and send it back to first and the time taken for the loop is shown. When i tried the examples, i didn't set the order of which module to transmit and which one to receive and i was wondering why it was not working! So to make simple i made two sketches which are 99.9% similar except in the rad and write variables. Upload SKETCH1 to the first one which is kept connected to a computer and keep arduino serial monitor open. The second one is on an arduino which is kept far from the computer and can be powered with a 9volt battery. The second one receives the signal and relays it back to the first one and the response can be viewed on a PC. So to make it simple, load sketch2 to an arduino with nrf24l module and then keep it away from pc with a 9v battery attached. Then load sketch1 to the next arduino and open its serial monitor to see the response.
SKETCH2 (for the arduino which will send pback the response, battery powered)
/*
Copyright (C) 2011 J. Coliz <[email protected]>
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"
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
//
// Hardware configuration
//
// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
RF24 radio(9,10);
//
// 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;
int led = 8;
void setup(void)
{
Serial.begin(57600);
analogReference(INTERNAL);
radio.begin();
// optionally, increase the delay between retries & # of retries
radio.setRetries(15,15);
radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1,pipes[1]);
//
// 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();
bool ok = radio.write( &time, sizeof(unsigned long) );
// 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) );
// 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();
}
}
}
SKETCH1 (for the arduino connected to the computer with serial monitor and this keeps transmitting to the arduino with SKETCH2 loaded and listen for the reply)
/*
Copyright (C) 2011 J. Coliz <[email protected]>
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"
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
//
// Hardware configuration
//
// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
RF24 radio(9,10);
//
// 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);
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 ( 1 )
{
char c = toupper(Serial.read());
if ( 1 )
{
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[1]);
radio.openReadingPipe(1,pipes[0]);
}
}
}
When I connected the Arduino to external power source ( 9 V battery ) the receiver didnt work and when I connected the receiver to the computer it worked perfectly. Can you help me to solve the problem
ReplyDeleteMight be a grounding issue. Try connecting the Arduino ground and earth ground (water pipe, mains earth connected device case)
DeleteBut idk.
What is the SS you're referring to? none of the chips have an SS pin
ReplyDeleteIt is CSN (chip select/ Slave Select (SS) in spi)
DeleteI tried this too with an Uno and a Micro. The Uno is transmitting just fine. But the micro isn't doing anything. It is using the sketch2.
ReplyDeleteCould be some pin difference. Check port commands on uno vs micro. PORTB commands are specific to microcontroller
DeleteHi RiY,
ReplyDeletei didnt understand the output on the scop:
Starting Poor Man's Wireless 2.4GHz Scanner ...
Channel Layout
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <
| :: : :: : W : | 4
| W++ | 2
| . .W . | 6
| - W - | 3
| - W - | 3
| + +++++ ++++ + W W+ | 2
| -. -...-+--.. -. ... .....W. . . | 6
| W W W W W W W WWW | 1
| + +W+WWW++++++WW++ +++ + + | 2
| +::::W+++:aa:+ :: | 4
| ++ W + + + + + + | 2
| : :: : :W ::* | 5
| . : .W. .... . | 7
| +W W W + + W | 2
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <
| - - - W- * - -- | 3
| - -- - W- - - | 3
| W WW W | 1
| W++ | 2
| --- --*---- W - - | 3
| -- --- -- ***W---*W**** - - | 3
| WW W WW | 1
| W W + + | 2
| W : : : | 5
| +W + + + + | 2
| + ++ + + +W + | 2
| * W - | 3
| + + + +W ++ + + | 2
| + + + W + + | 2
can you please explain?
thanks
it (probably)showing signals from nearby wifi routers. See the code : char grey[] = " .:-=+*aRW";
DeleteSo it shows a . for low signals and a W for stronger ones. You can try changing the loop count in the function void scanChannels(void) ie (in for( int j=0 ; j<200 ; j++) ) from 200 to a lower value for e.g use for( int j=0 ; j<100 ; j++) and see if it shows less clutter
Also double check there is no loose connection at the connectors and breadboard (if used)
thanks for you fast respond.
Deletewhat i should see on the scop to be sure that my transmiter responding and speaking with the exact receiver that i wanted to?
Thank you very much for this post. I have expanded your post adding a joystick at the transmitter and a couple of servos at the receiver : http://binefa.cat/blog/?p=122
ReplyDelete"'RF24' does not name any type"..
ReplyDeleteAny ideas?
Install library from https://github.com/maniacbug/RF24 , download zip file, extract to arduino/library folder
DeleteI am trying to build setup having 1 transmitter and 2 receivers , Is it possible with nrf ? if it possible can you please send me some simple sketch of transmitter.
ReplyDeleteThanks for the examples, Riya! I am able to build them and see the response. A couple of observations: first I had to change baud rate to 9600 in sketch1 otherwise the terminal characters would turn into gibberish after a few normal outputs. Second, around 50% of the responses failed even though the sketch2 module is only 3 meters away. As I am trying to build a rolling code remote based on your other article:
ReplyDeletehttp://www.riyas.org/2015/07/a-simple-rf-remote-with-secure-rolling-code-and-arduino.html
Would these errors be a problem in that project once I adapt it for NRF24L01?