Monday, April 8, 2013

Tutorial - nRF24L01 and AVR

To start out with, you have to know that I am just a hobby programmer so if you find errors or possible improvements in my code, please give me a note so that I can correct them.

If you have read my last blog posts IR-RF remote control and Temperature based flow regulator you have noticed i like to add wireless control to my components. In this tutorial i will describe how i managed to get the nRF24L01 module to work with AVR microships like the Atmega88 (28pin), ATtiny26 (20pin) and ATtiny85 (8pin), since almost all of the tutorials out there are aimed at the Arduino users.

The nRF24L01 module is an awesome RF module that works on the 2,4 GHz band and is perfect for wireless communication in a house because it will penetrate even thick concrete walls. The nRF24L01 does all the hard programming fore you, and even has a function to automatically check if the transmitted data is received at the other end.
There are a couple of different versions of the nRF-family chips and they all seem to work in a similar way. I have for example used the nRF905 (433MHz) module with allmost the same code as I use on the nRF24L01 and the nRF24L01+ without any problems. These little modules has an impressive range, with some versions that manages up to 1000 m (free sight) communication and up to 2000 m with a biquad antenna.

nRF24L01 versus nRF24L01+ 
The (+) version is the new updated version of the chip and supports data rate of 1 Mbps, 2 Mbps and a "long distance mode" of 250 kbps which is very useful when you want to extend the broadcast length.
The older nRF24L01 (which i have used in my previous posts) only support 1 Mbps or 2 Mbps data rate.
Both the models are compatible with each other, as long as they are set to the same data rate. Since they both costs about the same (close to nothing) I would recommend you to buy the + version!



The module
An ebay search on "nRF24L01" shows that there are many different versions of the modules that has the nRF24L01(+), and I have read that some of them are better then others due to better grounding and so on. But if you are after the long-range ones, make sure it has the + sign, and buy one with an extended antenna like this one:




Part one - Setup
Connection differences
The nRF24L01 module has 10 connectors and the + version has 8. The  difference is that the + version instead of having two 3,3 V and two GND, have its ground (the one with a white square around it) and 3,3 V supply, next to each other. If changing module from a new + version to an old one, make sure not to forget to move the GND cable to the right place, otherwise it will shorten out your circuit.

Here is a picture of the + version (top view), where you can see all the connections labeled. The old version has two GND connections at the very top instead of at the down right corner. 

Power supply (GND & VCC)
The module has to be powered with 3,3 V and cannot be powered by a 5 V power supply! Since it takes very little current I use a linear regulator to drop the voltage down to 3,3 V.
To make things a little easier for us, the chip can handle 5 V on the i/O ports, which is nice since it would be a pain to regulate down all the i/O cables from the AVR chip.

Chip Enable (CE)
Is used when to either send the data (transmitter) or start receive data (receiver).
The CE-pin is connected to any unused i/O port on the AVR and is set as output (set bit to one in the DDx register where x is the port letter.)
Atmega88: PB1, ATtiny26: PA0, ATtiny85: PB3


SPI Chip Select (CSN)
Also known as "Ship select not". The CSN-pin is also connected to any unused i/O port on the AVR and set to output. The CSN pin is held high at all the time except for when to send a SPI-command from the AVR to the nRF.
Atmega88: PB2, ATtiny26: PA1, ATtiny85: PB4

SPI Clock (SCK)
This is the serial clock. The SCK connects to the SCK-pin on the AVR.
Atmega88: PB5, ATtiny26: PB2, ATtiny85: PB2


SPI Master output Slave input (MOSI or MO)
This is the data line in the SPI system.
If your AVR chip supports SPI-transfere like the Atmega88, this connects to MOSI on the AVR as well and is set as output.

On AVR's that lacks SPI, like the ATtiny26 and ATtiny85 they come with USI instead, and the datasheet it says:

"The USI Three-wire mode is compliant to the Serial Peripheral Interface (SPI) mode 0 and 1, but
does not have the slave select (SS) pin functionality. However, this feature can be implemented
in software if necessary"

The "SS" refered to is the same as "CSN"
And after some research i found this blog that helped me allot.

To get the USI to SPI up and running I found out that I had to connect the MOSI pin from the nRF to the MISO pin on the AVR and set it as output.
Atmega88: PB3, ATtiny26: PB1, ATtiny85: PB1


SPI Master input Slave output (MISO or MI)
This is the data line in the SPI system.

If your AVR chip supports SPI-transfere like the Atmega88, this connects to MISO on the AVR and this one stays as an input.

To get it working on the ATtiny26 and ATtiny85, i had to use USI as mentioned above. This only worked when I connected the MISO pin on the nRF to the MOSI pin on the AVR and set it as input and enable internal pullup.
Atmega88: PB4, ATtiny26: PB0, ATtiny85: PB0


Interrupt Request (IRQ)
The IRQ pin is not necessary, but a great way of knowing when something has happened to the nRF. you can for example tell the nRF to set set the IRQ high when a package is received, or when a successful transmission is completed. Very useful!

If your AVR has more than 8 pins and an available interrupt-pin i would highly suggest you to connect the IRQ to that one and setup an interrupt request.
Atmega88: PD2, ATtiny26: PB6, ATtiny85: -



Part two- Programming
Here i will explain the c-program that runs on the AVR-chip. You can find a working copy of my code here (easier to copy and paste from).

Includes
I have included these lines to get my code to work:








You can see that i am importing a file called nRF24L01.h. This is a small library that defines the registers of the nRF so that i for example can call register "STATUS" instead of the register "0x07"... Just copy the text in the link and paste it into a file that you name "nRF24L01.h" and put it in the root of your folder.

Defines
To make the code cleaner i also put these definitions in the "nRF24L01.h"-file:







And also add the defines:
#define W 1
#define R  0



SPI
Initialization
The nRF chip communicates with the AVR-chip using SPI which has to be initialized in the AVR according to its datasheet. Here is the initializing code for Atmega88:















ATtiny26:
















ATtiny85:
















Communication
Now to send and receive a byte from the nRF with the SPI all you have to do is to use this function:
Atmega88 (SPI):
ATtiny(26 & 85) (USI as SPI):















I don't think it matters if you send a char or an integer, this is just how i got it to work... Note that these functions always returns something, this returned message is only cared fore when reading data from the nRF (a more appropriate name of the function might be "Write_Read_Byte_SPI").

nRF24L01(+) communication
Now to the fun part...

How it works
1) The nRF starts listening for commands when the CSN-pin goes low.
2) after a delay of 10us it accepts a single byte through SPI, which tells the nRF which bytes you want to read/write to, and if you want to read or write to it.
3) a 10us delay later it then accepts further bytes which is either written to the above specified register, or a number of dummy bytes (that tells the nRF how many bytes you want to read out)
4) when finished close the connection by setting CSN to high again.

Reading bytes from nRF
To start off, make sure your SPI communication is working by reading out something from the nRF. Reading a register on the nRF is accomplished by this function: (all of my example codes is for Atmega88, just change to the right port and pin number for the CSN and CE to get it to work on ATtiny as well)













I recommend you to start out by reading the STATUS register like this:

USART
If you have an AVR that supports USART like the Atmega88, i highly recommend you to use that as a way of sending the data back to the computer with this little friend... (I have written a small tutorial in the subject)



This is done simply by calling the function like this:



If you like me have a function called "USART_Transmit(uint8_t data)"
The usart should send 0b00001110 (or 0x0E) to the computer since it is the preset configuration of the STATUS registry (see the end of this blogpost).

LED
If you are using an ATtiny it lacks the USART and thereby the ability to write things back to the computer, then you can use a more hardcore way using an LED to turn on if the STATUS register is set correctly. Since the bites in the STATUS (0x07) register are preset to 0b00001110 (or 0x0E) you can test if this is true by this function:








Make sure you remember to first set the LED-pin to output: DDRB |=(1<<5);

If you are using an 8-pin ATtiny like the ATtiny85, there is not a single free pin on the chip to put the LED on, so i think a good idea would be to use the MISO-pin as a temporary LED output (since it is an output already and the SPI is not in use at the moment). Attach the LED to the PB1 and via a resistor to GND, then in the if-function above change the port number to 1. I haven't tested this my selves but i doubt it would cause any problem to the SPI-connection.

Writing bytes to the nRF
Now it's time to send a command to the nRF, this is done almost the exact same way as the reading command with this function:











If the register holds more then one byte, the TX_ADDR-byte for example holds five bytes, then you have to send them one at a time after each other with 10us delay in between. This makes the function a bit more complicated since C-code is unwilling to pas arrays of integers into functions as is.

I also wanted to clean up my code a bit, so I decided to make one function that I can use to both read and write to the nRF. The function should also accept an array of integers and be able to return an array of integers. This is the result:
































The most confusing thing whit this function is the W_TX_PAYLOAD in the if-statement... The thing is that when you want to write bytes to the W_TX_PAYLOAD you cannot add the W_REGISTER as you normally does when you want to write to a register. Have a look at the registry setup at the very bottom of this blog post, and you will see that the W_REGISTER and the W_TX_PAYLOAD is in the same "top level" of the registers. The same goes for the TX_FLUSH registers...

Now here is some examples that shows how to use the function:


To come back to the W_TX_PAYLOAD, when you want to ad the payload to the nRF, you simply use the "R" instead of the "W" to trick the WeiteToNrf-function to not add the W_REGISTER.

Setting up nRF24L01(+) 
Now it is time to setup the nRF for your specifications. In the example codes, I will send a 5 byte payload with the nRF. This is easily changed to a 1-32 byte payload by changing a bit in the initialization step of the nRF (see below)... This is how i usually set it up for simple communication between two nRF's:
















































In the code above, i missed a very important setting (if using EN_AA) that sets the number of retries and the retry delay like this:




Add these lines in the function above to set the number of retries to 15 and the delay to 750us... the delay has to be greater than 500us if you are in the 250kbps mode, or if the payload is greater than 5bytes when in 1Mbps-mode or a payload greater then 15bytes when in 2Mbps mode. Note that the default value of this is only 250us, and will therefore cause trouble when in 250kbps mode and with bigger payloads!

As you can see i decided to make it a transmitter this time. This is easily changed in the code by first delay 50ms, to make sure the nRF is in sleep mode, then send 0x1F to the CONFIG register, than make it wait 50ms again before the first receive-command.

Transmit data
When in transmitting mode this is the function that sends your payload:













And you call the transmit function like this: (now i just send 0x93 five times in a row, you can fill the array with any bytes you want to send)








Receive data
When in receiver mode this is the function that listens and receives your data:









 After every received/transmitted payload the IRQ's in the nRF has to be reset in order to receive/transmit next package. This is done like this:











Verify transmitted/received data using Interrupt
If you have more than an 8-pin AVR, I say use an interrupt to get triggered when data is successfully received or transmitted. INT0 interrupt is setup like this:

First you have to initialize the interrupt like this on Atmega88:










And initialization on ATtiny26:










Interrupt caused when receiving data
Setup a function triggered by the corresponding vector (INT0) at the very bottom of your code like this: (the global array "*data" is at the very top of my code...) And here i use it to send the received payload to the computer by usart:




















Interrupt caused on transmission success
Use the same interrupt function when transmitting data but change its content to just flash the LED to tell you that transmission completed, or you can tell the nRF to switch for a receiver, if what you just sent was a question to a receiver that in turn changed to a transmitter to return your call.

When you use interrupt, it is crucial that you enables the external interrupts by the command sei(); This should be done before the receive_payload, and transmit_payload is used.

Verify transmitted received data without interrupt
If your chip lacks interrupt or if you ran out of free interrupt pins, you can manually check if the IRQ-flags are set in the status register after every time you either transmit data, or run the receive_payload function (before the reset function!!!)

Transmitting
The easiest when you want to see if transmission succeeded or failed, is to check if the MAX_RT is set which mean it failed. This is done like this:


Then you know you have to resend the package.... i usually put this statement in a while loop that loops untill the package is received!

Receiving
Do the same check to see if no data is received by checking the RX_DR-bit (data-ready) like this:



Or it might be a better idea to check if data is received by changing "!=" to "==", if not, you start the receive_payload function again!

Code overview














































If you are using the devise as a transmitter, I usually have an USART-interrupt function called "ISR(USART_RX_vect)" at the very bottom that triggers when the computer sends something to the microchip. In the usart interrupt vector it then calls transmit_payload with the data received from the usart.

If you don't use usart, in the main while loop, send the data as described above "Transmit data".

If you don't use the  INT0-interrupt to check if the transmission succeeded  put an if-statement in the main while loop to check whether the correct IRQ flag (nr 4) in the STATUS register is cleared as described earlier.

If your chip is in receiver mode, in the main while loop, i usually call:
while(1)
{
       reset();
       receive_payload();
}

And if i don't have an interrupt, followed by an if-statement to see if anything was received by checking if the correct IRQ flag (nr 6) is set!

Long range mode
If you have the + version, than you can set the RF_SETUP byte to 0x27 instead of 0x07, which will enable the 250 kbps mode (long range) on full power, and i also recommend you to read this tutorial on how to build an amplifying biquad antenna. (remember to set the EN_AA-delay to at least 500us as described above)

Registers
This is straight from the datashet of the older version of the nRF24L01 (i find it easier to read thean the + version)

This is the layout of the "top level" registers as i call them:



And here are all the other registers and there configurations:



I hope you enjoyed my tutorial...
as always, if there is questions there might be an answer in the comment field.
/Kalle

275 comments:

  1. Kalle, great tutorials! Will use a Raspberry to control my sun shades (and then Nexa switches, etc) - the information you provide on your blog is spot on. Thanks

    ReplyDelete
    Replies
    1. Thanks, glad to hear someone has use of the blog =)
      If you have any questions just post them here, and i might be able to help!

      Delete
    2. Have a look at my new blog post on how to control nRF24L01 with RPi with a python program (http://gizmosnack.blogspot.se/2013/05/raspberry-pi-nrf24l01-and-tcp.html)!

      very easy programming, and the hardware setup couldn't be easier (only needs a cable in between)!

      Delete
  2. great tutorial. unfortunately i still can't make my pair of nrf24l01 to communicate. i want to connect two atmega32 on two computers (thanks for the UART tutorial, it works) and send a caracter from a computer to another. hope i have enough brain to make this thing work

    ReplyDelete
    Replies
    1. Hi Dan!
      Where does it go wrong?
      Do you get the SPI-communication to work between the atmega and the nrf?

      Delete
    2. I made SS pin output HIGH. Now I can communicate with nrf24l01. It seems that i can transmit a character.Now the problem is to receive the character.
      I changed the config register
      val[0]=0x1F;
      WriteToNrf(2, CONFIG, val, 1);//the second nrf is a receiver
      And that's all. I do not know how to put on screen the character received. It must be something like USART_Transmit(the caracter received).

      Delete
  3. Ah, i realize now that i have forgotten to add the defines:
    #define W 1
    #define R 0

    in the tutorial above..
    i see you have put a "2" as read or write. in that case it will never return anything from the nrf (the if statement takes the 2 as a "Write" command)
    Put the defines in your code, and try calling the function:
    val[0]=0x1F;
    WriteToNrf(W, CONFIG, val, 1);
    on the receiver
    Yes send the data to the computer with usart_transmit(data[i]) (in a for-loop that increments the i from 0 to the number of bytes you have received -1)

    ReplyDelete
    Replies
    1. thanks for the quick answer. still no luck.
      the code for receiveris
      while(1)
      {
      reset();
      receive_payload();
      _delay_us(10);
      data=WriteToNrf(R,R_RX_PAYLOAD,data,1);
      USART_Transmit(data);
      }
      the code for transmitter (in the while loop) is
      reset();
      transmit_payload(0x90);
      and the data that I receive is 0x60. i do not understand why

      Delete
    2. Here is the if-statement i talked about yesterday that checks if anything is received (if the IRQ bit "RX_DR" (bit 6) is set, data is received):

      reset();
      receive_payload();

      if ((GetReg(STATUS)&(1<<RX_DR))!=0)
      {
      data=WriteToNrf(R, R_RX_PAYLOAD, data, 1);
      USART_Transmit(data[0]);
      }

      Delete
  4. Ok, first of all, there is no payload data to read from the nrf if the receive_payload didn't receive anything during its 1 second of listening...
    there is no point of reading out the payload if there is nothing in it!
    either you use the if-statement described above, or my advise is to use the interrupt (also described above) that triggers when the receive_payload actually gets a payload!
    In the interrupt, you then calls the "data=WriteToNrf(R,R_RX_PAYLOAD,data,1);"
    and because the "data" is a vector, i think that you even if you just sends one byte, have to send if to the computer by:
    USART_Transmit(data[0]); //(sends the first byte in the vector to computer)

    ReplyDelete
  5. hi, Kalle, this is great tutorial! But maybe I typped something is wrong, I use Atmega16a, nRF24L01+, But Now It seems that even the GetReg() function do not return correct answer!

    My code snippet in github gist: https://gist.github.com/van9ogh/5651555

    actually, when i read the GetReg() from serial, the value is 0x1E. sometimes it change to 0x1F!

    ReplyDelete
    Replies
    1. (sorry if you got my last reply, this is how it is suppose to bee:)
      Hi! I have checked your code, and I have a couple of questions:
      1. The reset(void) should be "0x70" (line 107) and not "0x07" (i see i wrote 0b70 (wrong) up in the tutorial...)
      2. Line 176 "}Kalle" ? yes that is my name ;)
      3. I highly recommend you to set the SETUP_RETR to "0x2F" as described above (When using EN_AA)
      4. Why do you initiate the interrupt-ISR-function when you are not using it (or even have the USART initiated)
      5. Actually the STATUS registry is suppose to bee 0x0E (thanks, I will change my tutorial, I mixed up the binary to hex when reading the data sheet...)

      When the "1" appears before the "E", it means that maximum number of retries has accrued (you must have tried to send something without a successful transmission (and EN_AA on!).
      When status turns to 0x1F, the F tells you that you have filled your TX FIFO (the cash that stores your payload!) when this happens, i guess the FLUSH_TX is not working..
      But your code does not call the Transmit-function, so this is strange!

      Delete
    2. thank you! Kalle, I have set SETUP_RETR to "0X2F", Now the GetReg(STATUS) return 0x0E!, You know, I have debugging this device for two days! this small progress make me so exciting!

      actually, I will use IRQ interrupt send data to computer as you say above, so the interrupt-ISR-function and USART is needed, but this is not related to this question, so I hide some functions.

      last, sorry for my careless in line 176 "}Kalle" ;-)

      Delete
    3. Sweet!
      Glad i could help =)
      I posted the code on github as well: https://gist.github.com/klalle/5652658 (easier to copy/paste)

      Delete
  6. Nice tutorial Sir,

    Btw would you like to send me your email??

    you can send here too

    jony.nambela@gmail.com

    thanks

    ReplyDelete
  7. I notice you use screenshots for your code. It's hard because you can't search it and can't copy/paste it. I assume the reason is for syntax highlighting. Consider Google's prettify: https://code.google.com/p/google-code-prettify/

    I use it all the time, see the code on: http://www.swharden.com/

    ReplyDelete
    Replies
    1. Thanks man, will look into that for my next posts! (I have posted links to my github page where you can copy and past from!)

      Delete
  8. code Tranceiver:
    http://www.mediafire.com/view/ih5a3k26xm6kci9/E3.c
    code Receiver:
    https://www.mediafire.com/view/eft42po1cbxe55u/receive.c
    I use pic 16f887.
    Can you help me find error in my project?
    Thanks Svara

    ReplyDelete
    Replies
    1. First thing i notice in the transmitter-code:
      line 341: W_buffer[1] = 0x90;
      You only sets the second bit ("1") in the 5-byte array... you want to set all the 5 bytes to something since you have declared on line 262-263 that you will send 5 bytes each time!
      have a look at this code:
      http://3.bp.blogspot.com/-Ea8Q1HoFwbM/UWLhxjsr1ZI/AAAAAAAAAsk/vQgZxZC0LF0/s1600/Capture21.PNG

      On line 345, why do you send 0x1e to the status register? it will wipe the MAX_RT flag (the "1"), and do nothing else ("e")..
      Since you clear the MAX_RT flag in the status register, line 350 will always bee false!

      hope this helped you somehow! (if you want to send only one byte at a time, change line 262 to one, and use W_buffer[0] = 0x90;)

      Delete
  9. Very good post.
    One question, I cannot understand why I should send "n" dummy bytes when I want to read something.
    10x

    ReplyDelete
    Replies
    1. "n" is the number of bytes... the nrf returns a single byte evert time you write a byte to it, which means you have to send 10 dummy bytes to receive 10 bytes from the payload for example....

      Delete
    2. I am a beginner in the uC world. Now I have read the SPI protocol and I understood that for each bit sent to the Slave, the Slave returns 1 bit.

      Delete
  10. Hi, sorry i am french so i will speak english with my "bad" english :D

    So, i have make your tutorial on ATTINY 2313A.
    I arrive to receive the STATUS register 0x0E
    I arrive to SET something like the TX adress etc (i have try to set, and read)

    But i don't understand, i make your code, when i start my microcontrolor, without making the NRF24l01 init, i can use that fonctions :

    void transmit_payload(uint8_t * W_buff)
    void receive_payload(void)

    I don't know if it realy works but with
    ((GetReg(STATUS) & (1<<6)) !=0) and ((GetReg(STATUS)& (1<<4))==1) verification it's look likes OK (but i doesn't verify with scope)

    Where i have a problem is when i start the function : void nrf24L01_init(void) :
    Then i can Receive always OK.
    If i try to send something : the program is like "stoped" or "paused" (it enter in the fonction "void transmit_payload(uint8_t * W_buff)" but doesn't go out !

    So i see that i have to change the CONFIG for be in TRANSMITER MODE, but if i see the datasheet i have just to put the value to 0x1F for be in TRANSMIT mode and 0x1E for be in received mode (for exemple)
    But when i call the void transmit_payload(uint8_t * W_buff) it do the same it enter in the fonction and the program is like "halted" i can't reay debug, i use serial communication for see where i go in, and i see that i go in the fonction and something crash in it ..
    Can you help me ? Did you know something that i doesn't know ? is there a special action to do for transmit something ?
    You can contact me at cybermath1@gmail.com but i can be connected on irc.freenode.org if you want

    ReplyDelete
    Replies
    1. I locate the problem, in the nrf24l01_ini() when i remove the part for shoose if the module is RX or TX :

      // val[0]=0x1F; //0b0001 1110
      // WriteToNrf(W, CONFIG, val, 1);

      It's realy curious, i will try now to see what is in CONFIG register by default

      Delete
    2. Ok now my problem is solved, if someone want, i can upload my code he is fully functionnal for ATTINY2313 Recepter and transmiter, thanks for your tutorial Gizmosnack and sorry for "flood"

      Delete
    3. Glad to hear you figured it out! please post your code on github (or anywhare else), and a link here, so that others can get helpe from your code!

      Delete
    4. Can you upload the code for Attiny2313 snoop

      Delete
    5. Is this code still avalible? or can be upload somewhere?
      At least thx for reply.

      Delete
    6. Has anyone this code ?
      Best regards,

      Delete
  11. nice to meet you!
    Sorry about my skill english. because it't terrible.
    why do we send 0x93 in code:
    "And you call the transmit function like this: (now i just send 0x93 five times in a row, you can fill the array with any bytes you want to send)"

    ReplyDelete
    Replies
    1. Hi!
      it was just an example!
      you change the 0x93 to the byte that you want to send!

      Delete
    2. ^.^ ! thanks so much.
      im very interestted in style your code. very clear and easy to understand

      Delete
    3. Thanks! Im not an expert programmer so i try to make it as simple as possible!

      Delete
  12. here my code : http://www.mediafire.com/?apmst7dmwmwa0sb
    Im using atmega16 with crystal 11.0592

    ReplyDelete
    Replies
    1. It's a bit time consuming to error check you code since you have your own way of doing things, but in general it seams like you have got the hang of it! I can't find anything wrong with it by just looking through it!
      Here is how i would do the error checking:
      1) make sure you have a working USART setup, so that you can send things from the atmega to the computor (to termite.exe or any other usart program)

      2) make sure the SPI is working between the atmega and the nrf (try reading out the status register and send it to the computer with the USART)

      3) Make sure step 1, and 2 is working on two setups, so that you can have one as a transmitter and one as a receiver (if you only have one USART=>USB, make sure the SPI works on both atmega by trying one at a time with USART)

      4) Configure on of the nRF to a receiver (with all the settings i wrote about in the blog), and confirm by reading out the "config" register to the computer. Set it to listen for data, and make sure to reset its status register regularly.

      5) configure the other nRF to a transmitter (with all the settings i wrote about in the blog) and confirm by reading out the config register.

      6) transmit something to the receiver, and after every transmission, read out the status register to see if it was successful or not!

      Good luck, sorry i couldn't find your problem...

      Delete
  13. thanks for your intructions!
    i'll try.
    thanks you Kalle!

    ReplyDelete
  14. Greatings from poland,

    Perfect tutorial,

    it has helped me a lot. I rewrote your code on smt32f4 as a transmiter and atmega 32 as a reciver (there were no changes in that one except from pins).

    Again excelent work. In next couple of days i will post my code if anyone have problems with stm32f4 ( i hope it will help someone someday)

    ReplyDelete
    Replies
    1. Karol, hope to see your avr code =)
      Kalle, great tutorial, im newble in avr, so trying understand it all))) also see your instruction about nrf && rpi, hope soon to connect mega32 with rpi =)

      Delete
  15. Hi Kalle!
    First of all: great tutorial :) However I've a little problem: my transmission fails all the time. (I don't use auto-acknowledgement, I just have the same adress on my both nRF.) When I check registers - they all look just like they should, so it can't be problem with communication with nRF24l01. My sending function, and configuration are the same as yours. Is the lack of auto acknowledgement a problem? Or shall it be with some other stuff?

    ReplyDelete
    Replies
    1. In fact it works, but only once - it looks like it's full after just one transmission. I tried flushing receiver manually after that, but still it won't work with me like I would love it to :) Sorry for multiple comments.
      Well, at least I know it's not issue with auto-acknowledgement, still the same problem after enabling it.

      Delete
    2. Hi!
      Glad to see this many people using my code =)
      Try reading out the status register on both the transmitter and receiver before transmission, after first successful transmission, and after second failed transmission and post the results here if you are having trouble understanding them... hopefully that will tell you if its a problem on the transmitter or receiver side and what it might bee.
      Good luck!
      /Kalle

      Delete
    3. It's very neat code :)
      I've my answer now - transmitter firstly send packet than on receiver in interrupt CE is being cleared and than... it newer went on again - so it was never ever listening again. It's like that in your code too, does it lack there, or it's meaningful?
      After this little change it works like it should :)

      Delete
    4. Congrats!
      Well, it is correct that i turn off CE in the receiving interrupt.
      The purpose of this is to turn off the listening for new data, so that i can read out the received data and not having to worry that new data will be received during the read out, which would corrupt the last package...
      The CE pin is turned back on, when i call the "receive_payload();" function, which on a receiver is done in every loop in the "main loop"...

      Delete
    5. hi sir,
      I have nrf24l01+ long range module, it is working fine in straight line of sight (300 m) but not working perfectly when i used in industrial environment due to obstracts, pillars and walls. while my required range is maximum 200 meters. Please guide me if i made any mistake and which one is better for mine nrf24l01+ or nrf905. Because gsm band also use 800 to 950mhz freq. Waiting for ur prompt reply
      thanks

      Delete
    6. Interesting!
      Well, i am not an expert in radios but too me it sounds like the 905 should be the better one in long range since it is working in the MHz freq compared to the 24l01 which uses 2,4GHz. Have you tried the 488MHz setting on the 905?
      If that doesn't work, try making a biquad antenna like this one: http://martybugs.net/wireless/biquad/ And make sure you are tilting them the right way!
      Looking forward to hear how your setup comes out in the end! =)

      Delete
    7. Another solution would be to put a transceiver in between your transmitter and your receiver, and have it resend the data it receives from the transmitter, to the receiver, that way there is no limit in how far you can reach! Good thing these little gadgets are so cheap =)

      Delete
  16. Thanks for your suggestion sir,
    Actually I m facing problem with obstacle, pillar . Range is too good of nrf24l01 which i have tested up to 400 m in air without obstacle. But in shed of textile loom machines it sometimes work or sometimes not due to iron stands, pillar, and other industrial equipment in the shed. Range is not issue, obstacles are creating problem.
    My query is how mobile signal works between lot of walls, conjusted places. Why we cannot use same frequency to as mobile phone. Which module will be better for this purpose.

    ReplyDelete
  17. I don`t have nrf905 module, if you suggest then will purchase.
    thanks

    ReplyDelete
    Replies
    1. Hi!
      200 meters indoor is quite allot! Did you say the transmission succeeds when the machines are not in use? if so, i'm impressed!

      As i said, i have worked with both the 24l01+ and the 905 (488MHz), but have not tested the range difference... If you decide to try with the 905, it works on almost the same way as the 24L01, even if you need to connect a few more pins to get the 905 working. This shouldn't bee a problem if you understand the code for 24L01 and read the manual (https://www1.elfa.se/data1/wwwroot/assets/datasheets/07300809.pdf), but if you need help, i have a working code...

      Anyway my best advice would be, as i said, to use a third of your 24L01+ and put it as a node in between the two you want to connect, and have it receive the payload from the transmitter, and forward the payload to the receiver. This would half the distance, and hopefully be enough. If not, use a forth node, or a cable ;)
      Good luck

      Delete
  18. Hi! Very nice tutorial!

    I would like to make a low consume repeater (a device that sends to other devices whatever he receives from another). I did it by using an arduino pro mini (atmega328) and MIRF library but now I want to try using atmega88. Thanks to your tutorial I learn a lot about the internal operation of the nrf and now I'm wondering some things:

    1. Can I use one channel for read and other channel for write at the same time?
    2. In my actual design with atmega328 I use the IRQ signal to wake up the atmega and when message is repeated go to sleep. Do you think that this behavior could affect your code?
    3. I need to save as much energy as I can (my devices are solar powered) but reading the datasheet I didnt found anything to save energy in RX mode. Do you know something about that?

    Thank you!

    ReplyDelete
    Replies
    1. Hi, and thanks!
      I'll try and answer your questions, but remember that i'm not an expert in these modules, and havn't been working with them for a while ;)

      1. Yes that should work, just use different addresses for receiver and transmitter. Make sure you setup the Auto ACK the correct way (read page 13-14 in the data-sheet, especially the picture on top of page 13 and nr 2 on page 14: https://www.sparkfun.com/datasheets/Components/nRF24L01_prelim_prod_spec_1_2.pdf)

      2. That would definently bee a good idea! set the Atmega to wake on interrupt caused by the IRQ pin on the nRF.

      3. Ok, if you don't need the range, go with the older version of the nRF and set it to the 1000 kbps mode. it takes 11,8 mA when set to 1Mbps and 12,3 mA in 2Mbps mode, see page 5. (The nrf+ version takes 12,6 in 250kbps, 13,1 in 1Mbps, and 13,5 in 2Mbps mode, see page 14 in its datasheet)

      an alternative option would be to have the receiver in powered down mode ~95% of the time (guessed this value), and have it started by the atmega on a short intervall only to listen for data... this might work if you set the transmitters to try and resend failed package for maximum amount of time, and with long time in between every retry... but you would have to calculate on it to see that the wakeup-time for the receiver is less than the full cycle of transmitt-retries (or have a loop on the transmitter that manually retransmitts the data untill it succeeds)

      Good luck, please let me know how it turns out, and if you need more help, just post the question here.
      /Kalle

      Delete
  19. sir may I ask for your email?
    I need your help, I am still confused what is the difference between NFR905 with NRF24L01 ..
    and if your program that are above it can be used in nfr905??
    please answer ..
    thanks

    ReplyDelete
    Replies
    1. Hi! Sure you can have my email, but i prefere not to put it on my blog due to the risk of getting lots of mail with questions... If you post your mail, i will respond!
      The difference with the two rf-modules are that one uses 488MHz, and the other 2,9GHz to send data. You need to modify the code above slightly to get it to work on the 905, but not much!

      Delete
  20. Hi Kalle,
    today I found this tutorial and would be happy to find a working solution for the nRF905. Would you be as kind as to send the code to me?

    Thanks

    Jens from germany

    ReplyDelete
    Replies
    1. Here is code for the 905, First included h-file, then receiver, last transmitter... (use translate.google.com or something to understand Swedish comments): https://gist.github.com/klalle/83ec2a1a691523f2829f

      Can't promise they will work as is, as you see i was mocking with them august 2012, but i remember that i had no problem getting them to work with this code!

      Notice the TXE-pin (start SPI) and the PWR (Power) pin, which the 24L01 doesn't have, otherwise you use the same code as above!

      Delete
    2. I see now that the code i sent you is an older version of my program... When you have got that to work, use the "WriteByreSPI"-function from the blogpost above to easily send/receive array of bytes!

      Delete
  21. can you tell me the point that needs to be replaced ... because I am a new beginner and not very familiar with NRF905 but this relates to my final project on campus

    ReplyDelete
    Replies
    1. Well, see the code i just posted to "jensgulow" with the code to the 905: https://gist.github.com/klalle/83ec2a1a691523f2829f
      This is an older version of the code, so change the "WriteByteSPI"-function to the version i use on the 24L01 code above!
      /Kalle

      Delete
  22. If I want to try to do my work, NRF905 what should I do.?

    ReplyDelete
    Replies
    1. Sorry, i don't quite understand your question there...?
      Do you want me to give you school project examples of what is possible to do with the nRF?

      Delete
  23. Hi there. Nice tutorial.

    I am desperatly tring to make the NRF to work. I am using 2 pic 16f877a. I wrote my program in CCS C and i am able to read, write the internal registers of the nrf but i cant transfer any data between the PICs. Do you have any idea or hint?
    I posted here the source code:
    http://www.sourcepod.com/kvrgni03-21521
    and here the header for the nrf
    http://www.sourcepod.com/agmjuo15-21520

    This is a school project and i would be very grateful if you could help me!
    Thanks in advance!

    ReplyDelete
    Replies
    1. Your links does not seem to work... anyway, if you have got a working setup where you can transfer bytes to and from the nRF, you should have no problems following my examples above... here is a link to full code

      Delete
  24. My question is, does your program above can I use on nrf905 and if there are to be changed can you tells me the parts I need to changed.

    ReplyDelete
  25. I'm using Atmega8 to interface with nRF905 transceiver. How do i confirm that the transceiver is functioning?

    ReplyDelete
    Replies
    1. well, yes, the code i just gave you is written for the 905!
      you see if the transceiver succeeded by analyzing the Status-register!

      Delete
    2. This comment has been removed by the author.

      Delete
  26. I am using ATmega32 microcontroller with NRF24L01 RF.
    As per the two conditioning mentioned in your post to Verify Transmitted received data without interrupt. I have used 2 LED after "if condition"
    In transmitting, after if condition my first LED glows but in Receiving condition, after if condition my another LED does not glow.
    At present unable to track/test how RF transmitting & receiving. I wonder what could be issue here. Can please guide me on this.
    Appreciate if you could provides pointers on troubleshooting steps which will help me to know Whether RF signally is in-place or not.

    ReplyDelete
    Replies
    1. Well, hard to say whats wrong... post a link to your code (upload on github or something) and i will have a look...
      /Kalle

      Delete
    2. I did ask for your whole code, and I was specific to not post it here on the forum, but on github! try again!

      Delete
  27. I am using your above mentioned code as a reference. At present I didn't use USART and interrupt but used your "if"condition that you have mentioned above. Only change that I have made is with "while loop" condition and "dataLen" variable.
    Please find uploaded code on githhub(https://gist.github.com/regentrndt/8333913). Looking forward to your assistance on this.

    ReplyDelete
    Replies
    1. Ok, first thing i find is that both your codes sets the nRF to Transmitter mode! change line 225 in the rx to "0x1F" to make it a transmitter! try that and see what happens!

      Delete
    2. It should be "make it into a receiver"

      Delete
    3. Allso, if you tell the nrf to send 10 bytes, you have to load it with 10 bytes! see this: code
      you cannot just send a "3", that wont work if you dont reset the datalength to 1 byte first!

      and allso update row 274 in "tx" the numbers of payload to send to 10 not five like this: WriteToNrf(R, W_TX_PAYLOAD,W_buff,10);

      Delete
  28. Thanks for your prompt reply. I have done the changes as per you suggestion. At present my input supply is 2.5 V and it is in range between 1.9-3.3V. But what I could find here now for pin CE, MOSI and CSN voltage value is getting high i.e. 5.85V and its always constant for three mentioned pins. And IRQ voltage is also getting 2.05V.Unable to transmit and receive.

    ReplyDelete
  29. For your reference code repository : https://gist.github.com/regentrndt/8348648

    ReplyDelete
    Replies
    1. You have named the code "rx", this is a transmitter code! (0x1E) and you are transmitting things in the main loop...

      how are you supposed to see if you had a successful transmission if you dont add a delay after the success-diod is lit?
      When your code succeeds it will lit the led, and right away try and transmit another package, but the receiver is set to listen for package 1s at a time, so it will not accept any more packages until the reset() function is called after the receive function (that's why its good to use interrupt to stop listening when package is received), so the next transmission will fail => stop the diod...

      Add a 5s delay in between every transmission so that you have time to see if it failed or succeded and give time to the receiver to reset!

      Delete
    2. the reason you read 5.85v might be because your voltage meter is to slow to show these short times of 0v... you need an logical analyzer for that (see my other post on that: http://gizmosnack.blogspot.se/2013/03/rf-fjarrstrombrytare-hack.html)

      Delete
  30. On TX side 0x1E and RX side 0x1F used already. I have made the changes as you suggested but still no respond from RF communication. I have updated reset() call and given a delay of 5s in between every transmission. Still unable to track failed and succeeded transmission. Can you please give some more useful troubleshooting pointers.Thanks

    ReplyDelete
    Replies
    1. Are you sure your spi is working? try reading out a registry that you have set, and confirm that they are set correctly... This could be done by checking if for example the CONFIG registry equals 0x1E on the tx, if so, blink the led...

      You seem to have an LCD connected, why not use it as a debug tool until you have got everything up and running? print out GetReg(STATUS) to see if and when it changes. I would suggest that you start by using the lcd for debugging the transmitter, to see that it has the right settings, and then debug the receiver.

      Delete
  31. For GetReg(STATUS) I am continuously getting ‘0’ values. I am using nrf24L01+ , so I believe value for SETUP_RETR ,RF_SETUP, STATUS may be different from nrf24L01, if it’s true then how to define or where can I find these values. I have searched a lot for this but I didn’t find something for nrf24L01+ specific.

    ReplyDelete
    Replies
    1. Good that you have found a problem!
      No, these are the same for the + version (as well as the nrf905)!
      Its either your LCD that cannot print out the format that the GetReg returns (uint8_t ), or that you dont have a working SPI connection!
      Start by verifying that the LCD can print something like 0x93 (probably prints out as corresponding ASCII-char)... then try to get a working SPI (by printing out the STATUS-registry)
      And do this without calling nrf24L01_init();!
      Good luck!

      Delete
  32. This comment has been removed by the author.

    ReplyDelete
  33. hi, I've been trying to get the nRF24L01+ to work with Attiny2313. Can you please check my code and point me in the right direction. Thank you
    https://github.com/TheAsuraSlayer/nRF24L01_AVR.git

    ReplyDelete
    Replies
    1. I think you're not allowed to power up the nRF before you configure it, it has to be in power down mode! in your RX code you start by power up (setting CONFIG-registry to 0x1F), then you configure it... no reason to configure it every time you call the receive-function!

      Delete
    2. I did the changes you asked me to do. not working yet.im giving the link with the modified code.
      https://github.com/TheAsuraSlayer/nRF24L01_AVR.git

      Delete
    3. Try with a 100 ms (not us) delay after setting the config-registry, i know i had a problem with that before! (both on tx and rx) and 10 ms before and after every transmission...

      Delete
    4. Hey sorry had a festival over here in India for a couple of days, but I did some changes to the code, its still not working. Kindly check the updated code. Now the RX code gives me a DATA RECEIVED message continuously, but the RX_Payload is 0x00.
      https://github.com/TheAsuraSlayer/nRF24L01_AVR.git

      Should I add a capacitor between the power supply. Can you upload the code onto a MCU that you have and check if the code works fine.
      Thank you.

      Delete
    5. Well, now you ar checking if the 6 th bit is 0, which indicates that no data was received! change the "==" to "!=" in the if statement...
      try your SPI by reading the STATUS and send it to the computor with USART and make sure you can read and wright to the registers... (remember the computer might give you the ASCII-char if not setup correctly)

      You have skipped to setup the delay on auto ack, the "SETUP_RETR", as i write in the post, it is very important!
      I have no time to test your code, sorry!

      Delete
    6. Should SETUP_RETR be setup even in the RX code?

      Delete
    7. you are right, should not be needed!

      Delete
    8. Yes a capacitor is probably a good idea! especially if you use a linear regulator (they usually demands it)

      Delete
    9. yeah I am using a linera regulator, ill go out and get a couple of 100uf? capacitors and check, I even tried with one attiny2313 and an arduino as the receiver, did not work. I had high hopes for these transceivers. Just started out embedded programming as a hobby. :), thanks for te help so far, ill let you know if the capacitors do the trick, other than that I code looks right to you?
      P.S I read a few registers after writing them and the USI-SPI is working fine

      Delete
    10. WOW this is frustrating! added the capacitors and yet no success. Any other ideas?

      Delete
    11. Well, seems like you have to do some proper debugging!

      First of all, change the interval in between every transmission to something like 5000 ms, so that the receiver have time to reset after a successful reception. (now you send 2 package during the 1000ms time of listening for data)

      *Making sure the transmitter is working properly by reading out the STATUS register before and after each transmission with no receiver connected. This should now indicate a failed transmission every time. (do this before you call the reset)
      *Try connecting the receiver and see if it changes the STATUS on the transmitter.
      *Try disabling the AutoAck to see if thats causing the error...
      *Connect the usart to the receiver and read out STATUS before and after the receive-function with and without the transmitter.
      *try removing the FLUSH_TX in the send_data function

      Please give me the results of the STATUS registers in every case

      Delete
    12. Hey!! So gues what? I finally got it to work. I'll post a link to my code. Basically I still can't figure out why it wasn't working before. But here is what I think made the difference.
      1) Added a 1000ms delay after making any changes to the CONFIG registry.
      2) Configured the config reg to TX or RX mode first, then made the necessary changes to the nRF and then finally powered it on.
      3) Added a 100uf capacitor to the module using a linear regulator.

      https://github.com/TheAsuraSlayer/nRF24L01_AVR.git

      Thank you for the help, though I have to get some complex things working with this module, so will probably bug you again!

      Delete
    13. Congratulations!
      That's very strange that you have to add the 1s delay!? Are you sure you have to set the device to RX or TX before you do the rest of the config? I'd say it would make no difference at all to the device!
      Now that you have a working code, make a backup of it, and start modifying the code to try and find whats really causing the problem!
      *does the transmissions work at all without the capacitor? (if not, you probably still has stability issues...) in my setup the linear regulator datasheet tells me to use 2 capacitors of different ratings, one between the 0-5 and one between the 0-3.3V, cant remember which one is big/small tough.

      *If still stable without capacitor, try messing with your code, like deleting rows 151-153 in your receiver and run the code again... (they should not have to be set before)

      What is your project, i'm just curious ;)

      Delete
    14. You could also try and change the power supply, it might be flaky...

      Delete
    15. 1)In my opinion the linear regulator without a capacitor was the problem. It stopped working after the removed the capacitor. 2) Right now powered using the Prolific usb-serial module, it works without any capacitors. 3) I think anyone using these modules should be aware of the impact of bad quality power on performance. As a beginner I've learnt my lesson!! Anyways ill complete the project and post the code

      Delete
  34. This comment has been removed by the author.

    ReplyDelete
  35. Thanks for your information. LCD can print all the format of Hex Values. WriteByteSPI(Unsigned char cData) SPDR return '0' Value all the time, although I get data in 'cData' variable. From Comp123.

    ReplyDelete
    Replies
    1. Looks like your SPI is not working! try reconnecting everything!

      Delete
  36. Is there any input voltage difference between nrf24L01 and nrf24L01+ . The nrf24L01 chip itself should be powered at 3.3V.At present for nrf24L01+ also I am providing same range in between 1.9V-3.3V. Does nrf24L01+ demands change in supply input voltage. nRF gets peak power on transmission start so by trying for electrolytic capacitor to nRF power line will it be helpful.

    ReplyDelete
  37. Range is the concern in my project so I am using nrf24L01+ advanced RF. Do you have any nrf24L01+ RF specific examples for reference posted anywhere. Thanks

    ReplyDelete
    Replies
    1. Don't really know what you are talking about!?
      As i have said in the blog post, there is not much difference at all with the + version except that you can us the 250kbps instead of 1Mbps which is the lowest speed the old version can manage. This increases the maximum length dramatically! But you can use exactly the same code if you're not using the 250kbps setting!

      Delete
    2. See this datasheet on bottom of page 55 to set the 250kbps. Set the RF_DR_LOW bit in the RF_SETUP registry

      Delete
  38. Can you make articles about nrf905 thank you, i really need your help

    ReplyDelete
    Replies
    1. Well, no, because it is almost exactly the same as for the 24L01!
      I get the impression that you just want a working code, without understanding it!? A search which will give you this as first hit, which is exactly what you want!

      Delete
  39. Hallo Kalle!
    First of all i have to admit that this is a great tutorial:) You help me a lot but i still have some problem with communication:( Maybe you can help me becouse i have no idea what to do now. As i noticed nRF does not request interrupt so as i guess it has to be problem with transmission. Moreover, once I managed to get a stable connection but only once... Please could you look at my code and help somehow? SPI communication is OK... Please help if you can:)

    transmiter code: http://wklej.org/id/1279926/

    ReplyDelete
  40. i checked STATUS register of transmitter, right after power up status is 0x0E, when transmission is over STATUS register is 0x2E and after reset again 0x0E. Everything seems to be ok but nRF does not generate interrupt signal on IRQ PIN...

    ReplyDelete
  41. You seem to have understood the concept!
    I might be wrong, but in your main while loop you try and transmit one time, and if that fails, you go into this funcion:
    while(!(GetReg(STATUS)&(1<<5)))
    SETBIT(PORTD,6);
    Which is a never ending loop!

    Because your code never jups out of the loop you will never try a secound time, isn't that a bit useless?
    I would start by just transmitting your payload every 1000ms or something, (remember to reset in between) and reading out the STATUS registry after every time!
    something like this in the main while loop:

    while(1){
    reset();

    transmit_payload(Buffer);

    _delay_ms(100); //whait a while before you check the status, give the receiver som time...

    if(!(GetReg(STATUS)&(1<<5))==0){ //checks if TX_DS is set to "1" => data sent
    SETBIT(PORTD,6); //LED on
    _delay_ms(5000); //indicate successfull transmission with a loooooong LED
    }
    CLEARBIT(PORTD,6); //LED off
    _delay_ms(1000); //Whait 1s before next transmission
    }

    Post your receiver code and i'll have a look!
    Good luck

    ReplyDelete
  42. Hallo, i've already finished my project:) but thanks for your respond. I tried many different variants and the results was always wrong. The key was to first set nRF as transmitter or receiver and then at the end power nRF up:) now i think i understand nRF i 100%:) If anyone want to see my code just tell it:)

    ReplyDelete
    Replies
    1. Congrats!
      Well, i wouldn't say you understand it 100 % if you claim we have to set it as a transmitter or receiver before powering up... ;)
      My code works like a charm when powering up and set to transmitter is set in the same command...
      I'd love to see your code, and what you are controlling with it!
      /Kalle

      Delete
  43. I have no idea why it's not working your way... but now it works just fine:) without any problems:) It was student project at University. I'm using nRF's to control RC Car:) This is very simple project so setting nRF's was the main problem:)
    This is my transmitter code: http://wklej.org/id/1282756/
    I used analog controllers from old PS3 pad, read value from ADC in Atmega and send this values to car. Car code is very similar to the transmitter only I configure 2 PWM channels instead of ADC:)

    ReplyDelete
  44. Most of code is yours:P but i'm working on my own library for communication with nRF. I finished my project but i still want to improve it:) First of all i want to add manipulation arm:) i have to use second analog controller from my ps3 pad;p

    ReplyDelete
  45. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  46. Something went wrong....

    I'm very confused that my modules won't work. The transceiver sends 31 for the Config register and 14 for the status register back to the pc. So, i guess that the connection between the module and my AVR(ATmega328) is right. The Receiver sends 30 and 14 back. My code: http://wklej.org/id/1287033/ .

    ReplyDelete
  47. Hallo,
    Is This a decimal or hexadecimal notation? It looks like decimal then CONFIG register for transmitter is 0x1F (should be 0x1E) and receiver 0x1E (should be 0x1F). Status looks fine then 14 in dec is 0x0E in hex:)
    Regards

    ReplyDelete
    Replies
    1. All values are in decimal. Status looks indeed very normal. The weird thing is that it doesn't make sense if the receiver is online or not.

      Delete
    2. I've found the problem! Thank you very very much! I had this in my code:
      val[0] = 0b0001110;
      if(SendMode == Transmitter)
      val[0] |= 0b1;
      But the Receiver must be configured with the last bit set instead of the transmitter.

      Now a little question, can the receiver also send and the transmitter also receive data?

      Delete
    3. hmmm... i don't have much time to analize your code but youre sending STATUS at the beginning of main loop it's mean after reset, so the transmitter STATUS allways will be 0x0E. Is the led on PC4 is triggered any time on receiver? On receiver you can just read status in loop and send it via uart any time when status is different than 0x0E. In this way you will confrim any succesfull transmision.

      Delete
    4. Yes it is possible, you just have to change config register. For example when transmitter send some data you have to switch it to receiver mode and you can wait for data from second device. It simple but need good timing:)

      Delete
    5. Nice to see you solved the problem!

      I often use the transceiver as first a receiver, then change to transmitter, and then back to receiving mode. Very usefull when using it as a remote logger, and you dont want data collected at a certain intervall, but instead when called upon. Or if you want different kind of data depending on whatever you send to the logger.

      Sander: Your USART program on the computer is whats displaying your hex-values as decimal. If you're using Termite, i think it was just a matter of changing the settings under plugins to "Hex View" see: http://www.compuphase.com/images/termite_settings.png

      Delete
  48. This comment has been removed by the author.

    ReplyDelete
  49. hello! could you help me with my code?
    I was checking your code and testing but when i arrived to write registers, i wanted read the registers for see if i could modified them but always show the reset values.

    ReplyDelete
  50. here is my code http://alextejeda.blogspot.mx/

    ReplyDelete
    Replies
    1. Hi!
      You should post your code with correct indentations to make it easier to read!

      I'm not quite sure what you are doing there... the bottom part:
      data= WtoNrf(R,CONFIG,data,2);
      -Register "CONFIG" only contains 1 register, not sure what you will get when reading it out twice, but then you set the:

      PORTD = data[1];
      -Which is the secound read out register from the CONFIG, as i said it only contains ONE register...

      Not sure why you get the reset register when you do this though!?
      /Kalle

      Delete
  51. This comment has been removed by the author.

    ReplyDelete
  52. Hi Kalle,

    i was trying get work with this your code, but it seems things is not so easy.
    Im not using usart or iterrupts.
    i use just using two atmega 328 and two too nrf24l01.
    I tried to poll RX/TX register at while loop, when i see led's what happend.
    I can see nothing happened, TX register 1<<4 is always '0'
    and RX register 1<<6 is always '0' too..

    in follow code, i was moved the int main function on the top because its simlpe for my own eye... and brain :)

    Can you Kalle check this code and help me someway, i think this the only way how i get working the code someday.
    my code is at github, RX/TX file.
    https://github.com/satasata/rf

    im not sure that is the avr connected via spi to the nrf?

    Thanks Kalle!

    ReplyDelete
    Replies
    1. Hi!
      First thing i notice in TX code is that you define datalen=3 (row1 and row 156) and then you send 5 bytes (row209)...

      in RX, first same (datalen=3), then why the 1500ms delay on row 45? during that time you will not be listening to anything... remove that one!

      Good luck

      Delete
    2. Thanks for advices,

      i was remove 1 row /tx/rx sides datalen=3 both tx/rx side...
      and remove delaytime 1500ms at rx whileloop, row 45 (i was made this delay because of i was thinkin i give some time at nrf status to came change, seems that not necessary)
      and i made changes [datalen] to follow;
      RX row 112, "static uint8_t ret[5];"
      RX row 176, "val[0]=5;"

      TX row 96, "static uint8_t ret[5];"
      TX row 156, "val[0]=5;"

      and i can still see, change not happened at status what i trying to read...
      RX led is high all the time
      TX led is low all the time

      have you ideas how i test the spi communication to the nrf? is there possibly some standby-power-status register?

      Delete
    3. Well, to test if the SPI is working, read out the status registry bits one by one, and see what it sais... if its 0x00 or 0xff than you don't have a working setup...

      One way of showing the byte could be to connect 8 LED's to the chip (PORTB 0-7 for example) and set the PORTB = GetReg(STATUS);
      Otherwise you can loop through the bites in the STATUS and let the LED show each bit at a time:
      While(1){
      for(i=0; i<8; i++){
      if((GetReg(STATUS) & (1<<i)) != 0){
      SETBIT(PORTB, 0);
      _delay_ms(1000);
      {
      else{
      CLEARBIT(PORTB, 0);
      _delay_ms(1000);
      }
      }

      SETBIT(PORTB, 0); //to get LED-ON for five seconds in between every read out
      _delay_ms(5000);
      }

      Delete
    4. my atmel studio give a many warnings with that integer test and i cant make that code relevant.. so i was made this other PORTB test.

      i get next ressult. PB1, PB3 and PB5 was high and rest of portb was low.. mean working?

      Delete
    5. Good your SPI seams to be working!
      Bit nr 5 in status reg => TX_DS high (data is sent)...
      Bit 1-3 =>data pipe number for payload, means you have data to read..

      Delete
    6. Sorry this flooding but there is somethin what i dont absolutely understand..

      1. when i cheking the rx status 1<<6 at the following code, led is blinkin (i think this is ok but..).. The big suprice is that, when i take the tx side power off the led will blinkin ater that... so is that possible, the rx side 1<<6 register will switching between 1 and 0 even without there is no packets for receiving..

      2. wich register i must read, when i want read i.e receiving that "0x93", is it at getreg funkition 'reg' register or spi SPDR?

      here is the question 1 code:

      while(1)
      {
      reset();
      receive_payload();

      if (((GetReg(STATUS) & (1<<6)) == 1));
      {
      SETBIT(PORTB, 0);
      _delay_ms(200);
      }

      if (((GetReg(STATUS) & (1<<6)) == 0));
      {
      CLEARBIT(PORTB, 0);
      _delay_ms(100);
      }
      }


      and thaks you very much Kalle, this wreally do not so simple..
      I try to this message is last question..

      Delete
    7. I think you ar doing the check wrong... it should be "!=0" instead of "==1"

      Delete
  53. Hi,
    Can this code be used for interfacing NRF24L01+ with ATmega8L?
    Will these nrf modules work in mesh topology?
    i'm using this for a short range i.e about less than 50 meter wireless communication!
    Thanks in advance!
    -Goutham

    ReplyDelete
    Replies
    1. Hi!
      Yes, should not be any problem using this code with the Atmega8L!
      What kind of mesh topology are you planing to use? yes you could send data to several nodes and receive from several nodes with one nRF, so should not be a problem!
      /Kalle

      Delete
  54. Hello Sir your tutorial is very good..!
    but i have a question that whether calling reset() resets TX_ADDR..?

    ReplyDelete
    Replies
    1. Thanks!
      No, it only resets the bits in the STATUS-registry (see RX_DR, TX_DS and MAX_RT)

      Delete
  55. Thanx for your reply sir...

    Sir i need your help, i was trying to interface two NRF24L01+ with two different micros since last weak but don't know whats wrong with my code..
    If possible have a look at my code and please correct me...

    Transmitter Code: http://pastebin.com/vzYvkiXx
    Receiver Code: http://pastebin.com/i9dKX4F2

    I am not able to receive a single char in above code.

    ReplyDelete
    Replies
    1. I think you need to set the RX_ADDR_Po = TX_ADDR if the EN_AA is enabled (Which it is!)
      This is because the receiver will receive the package, and return it to the same address...
      /Kalle

      Delete
  56. But my SPI communication with NRF is working..!

    ReplyDelete
  57. Thanx for your reply sir... :D

    Sir but I am still not able to receiver data at receiver side.
    My SPI communication is working and i have checked it with GetReg() function by reading the seated values like CONFIG reg....
    Please sir help me out sir please....

    My code is given bellow..
    Receiver: http://pastebin.com/u8EfmTdu
    Transmitter: http://pastebin.com/cbYtiT00

    ReplyDelete
    Replies
    1. Try remove the 1000ms delay at the end of the while-loop on the receiver code... during that time, the transmitter wont be able to send you anything because you are not listening!

      Delete
    2. Sir, Now my NRFs are communicating and the problem was the bad power supply...
      Thanx sir for helping me ... :D

      But, Sir how to switch from Transmitter to Receiver ...?

      Delete
    3. Congrats! =)
      It's easy, just change the CONFIG-registry from 0x1E to 0x1F (this have to be done when in power down mode => 100ms after last transmission or reseiving-session)

      Delete
  58. Hello and greetings from Poland :) I am trying to communicate betweend two nRF24L01+ with ATmega8A as transmitter and ATmega32A as receiver. I am going to use it in RGB propeller display in future to send an image wireless. Right now I just want to send one byte stored from the beginning in program memory (I am not using USART) and put this byte on LED port.
    I am generally using your code, with just a little change on pins. However I can't make my transmission success. I did checking the registers like you wrote in one of your posts like this:
    check=GetReg(CONFIG);
    reset();
    if(check==0x1E)
    {
    while(i<5)
    {
    SETBIT(PORTB,0);
    _delay_ms(100);
    CLEARBIT(PORTB,0);
    _delay_ms(100);
    i++;
    }
    }
    It looks OK for the first but it seems that I can't read back RF_SETUP and SETUP_RETR registers (LED is not blinkig) or these registers have other values then I have stored in them. I laso tried to check if sending a message was succeed with this code:
    if(!(GetReg(STATUS)&(1<<5))==0)
    {
    SETBIT(PORTB,0);
    _delay_ms(1000);
    }
    CLEARBIT(PORTB,0);
    _delay_ms(1000);
    but it also failed (LED was not blinkig). I checked power supply and it is very stable so it isn't this. Can you please check my code? I will be very greatful. Here are my codes:
    http://wklej.org/id/1348350/
    http://wklej.org/id/1348352/
    Please help me.
    Here is my email if you would like to write to me directly: xadrez@o2.pl

    ReplyDelete
    Replies
    1. Hi!
      Your code is a bit hard to read due to formating, but i cant find anything wrong with it...
      First of all make sure your SPI is working both ways, it seams that you only succeed in reading from the registers, not writning to them!?
      Make sure your cables are wired correctly, and try and get this to work before you put too much work on the code!
      /Kalle

      Delete
  59. Hi Kalle,
    thank you for this post. Currently I'm using nrf24l01+ in my home alarm project. I'm using board with atmega16 and your code with little modifications. Here is the source: http://pastebin.com/aZUwMFn1 . I got a problem. When I'm checking STATUS register, the board send to me 0xFF, not 0x0E. Could you tell me where the problem is?

    With regards
    Pawel

    ReplyDelete
    Replies
    1. When the Status returns 0xFF it means the SPI is not working!
      Check your cables and voltages!

      Delete
    2. I forgotten to supply +3V3 regulator. Now I see in terminal 0x00.

      Delete
  60. Hello,
    thanks for nice tutorial.
    I m using NRF24L01+ for first time , with the ATmega328p ,
    I not able to read the STATUS register


    I m only using following functions just to read STATUS register so that I can confirm SPI is working,
    USART_Init();
    USART_Transmit();
    USART_receive();
    SPI_Init();
    WriteByteSPI();
    GetReg();
    int main(void)
    {
    int main(void)
    {
    clockprescale();
    InitSPI();
    usart_init();
    reset();


    char sta = 'D';

    while(1)
    {

    USART_Transmit('B');
    _delay_ms(100);
    USART_Transmit(GetReg(STATUS));

    _delay_ms(100);

    }
    return 0;
    }

    but not getting STATUS value on Serial monitor.

    plz help me..

    ReplyDelete
  61. Hello kalle.....struggling for past 2 days didnt get any sucess....plz give some specific suggestion to do..

    ReplyDelete
    Replies
    1. Hi!
      Why do you have double main-functions?
      Does your USART work when sending the "B"?
      Are you sure your serial program is setup so it can show the STATUS byte as decimal or hex and not ACSII (http://www.jimprice.com/ascii-0-127.gif)
      Your SPI is obviously not working! check you cables and try again! are you supplying a stable 3,3V to the nrf?

      I'm not much help before you have a working SPI, try a different tutorial on how to setup the SPI on a Atmega328p!

      Good luck
      /Kalle

      Delete
    2. This ASCII-table is easier to read: http://www.asciichars.com/_site_media/ascii/ascii-chars-landscape.jpg

      Delete
  62. Hi Kalle!
    Great blog and big respect for spreading knowledge and helping everyone!
    I'm trying to communicate between 2 Atmega8 chips, like hello world. Just to send bytes from one and recieve on the other. SPI is working.
    What I did is took your code, modded the config register (1E/1F) and added transmit/recieve loops for transmitter and reciever. Is that all?
    It's not working and it's hard to debug deeper without USART (still incoming from ebay)...
    If you have time please check code here https://github.com/jivkovic/RF/ I really don't know what could be the problem, if code is ok I will try to add capacitor after linear regulator, I'm out of ideas..
    After this works I'd like to tidy up code so you can add it as the most basic hello world version (without interrupts, usart,..) if you want. I think it's much easier to learn for people new to uC world that way.

    Thank you for your time

    ReplyDelete
    Replies
    1. Hi!
      You seam to have understood the code and your code seems to be ok!
      Try and read out some other register in your test-code to se if you actually succeded to write something to the nrf:
      if (GetReg(SETUP_RETR) == 0x2F)
      {
      blinky();
      }

      My experience is that i do not need the capacitors on the linear regulators... maybe it depends on wich one you are using!?

      About the tidy hello world-code: I have thought of doing that, but have never got so far... and i think there is a point of letting people read this blog-post very carefully and learn the code instead of getting a working hello-world code. I think the working code would lead to me getting a million questions on how to modify the code to there needs, which is very easy if they have read the blog post and managed to get there own code working!
      /Kalle

      Delete
    2. It seems that writing succeeds after i started using 5V regulator on one, and 5V from usb on second chip.. makes no sense for me, I think regulator should handle both..
      Now only one more problem, verifying recieved data..
      Code above always go on the "else" part:
      if (((GetReg(STATUS) & (1 << 6)) != 0 ))
      {
      data=WriteToNrf(R, R_RX_PAYLOAD, data, dataLen);
      if (data[0] == 0x93)
      blinky();
      else
      blinky2();
      }

      tried comparing it to 0x00, 0xFF, and array of 5 0x93's sent from transmitter, but no success. Any idea?

      Delete
    3. Wow how frustrating :) Regulator IS working for both chips. I was getting blinks on reciever because USB voltage was messed. No data was actually recieved. I checked if (GetReg(SETUP_RETR) == 0x2F) after every sending, and it's ok. Tried removing the auto_ack and flush_tx from send function and still no luck. Also, tried with one nRF on raspberry like on your other tutorial, and everything seems fine, but without any sign of successful transmission. Do you have any other idea what else should I check? I really think it's about code.

      Delete
    4. Sorry for multiple comments. Checked TX: before sending, STATUS register is 0x0E, after sending 0x1E. Tried init routines from people who posted their working code in comments, still the same.

      Delete
    5. I'm sorry, I've gone through your code, and havn't found anything wrong with it!
      Try removing your components and reconnect everything (you probably have allready done this about 10 times...)
      /Kalle

      Delete
  63. This comment has been removed by the author.

    ReplyDelete
  64. I have these units but have no idea how to get them working. should i download the same code on both mega88. secondly can i connect an encoder and decoder on the USART port for communication.

    ReplyDelete
  65. I have downloaded compile your code and load to two mega88 and connect two nRF24L01, however i have no communication between both devices. what was your setup when you worked with these devices.

    ReplyDelete
    Replies
    1. Hm, have your read the post above? I think i have explained everything quite clearly... You have the same base code on both the devices, but call different functions if you use the chip as a receiver or a transmitter. And you have to set the CONFIG registry accordingly (1F = receiver or 1E=transmitter)
      Good luck

      Delete
  66. hi
    very nice tutorial.I am using atmega32 interfaced with nrf24l01+.but I am facing a problem in transmission.my getreg(status) always show 0 value.plz help me to solve this problem.my code is on https://github.com/missss/nRF24L01--interface-with-atmega32-problems/tree/master.

    ReplyDelete
  67. Kalle,

    My transmitter seems to be working ok, but there is a problem with my receiver. what modifications can you suggest for the receiver side?

    ReplyDelete
  68. I received data but it take time to receive.I set RF_SETUP to 0x27,disabling the auto acknowledgment and sending 5 byte data.Please help to solve this problem.

    ReplyDelete
  69. This comment has been removed by the author.

    ReplyDelete
  70. Hello there!
    Thank you for your wonderful tutorial.
    I have a question.
    The transmission mode and receive mode are normally work well.
    However, the event 'MAX_RT' is an error receive mode is not executed anymore.
    Only turn on the power again is solved.
    What's the problem?

    ReplyDelete
  71. Hello, thank's for this tutorial.
    I just have a note for the 8-pin chips, where you suggest to use the MISO-pin as a temporary LED output.

    According to its datasheet, when you select Three-wire mode (USIWM1 = 0 and USIWM0 = 1) the PORTB register doesn't control the port pin that was overrided by DO (MISO) anymore, so trying to use that port as a LED outuput won't work.

    So, instead of setting the USIWM0 inside initiSPI function, I set it inside WriteByteSPI() function, like this:

    uint8_t read_write_spi(uint8_t data)
    {
    USIDR = data;
    USISR |= (1<<USIOIF);

    USICR |= (1<<USIWM0);

    while((USISR & (1<<USIOIF)) == 0)
    {
    USICR |= (1<<USITC);
    }

    USICR &= ~(1<<USIWM0);

    return USIDR;
    }

    Then you have a free output pin to work around when you are not communicating with the nrf.

    ReplyDelete
  72. Hello Kalle!

    I tried to run your code on Atmega8 but I keep receiving something else than I transmitted (e.g. I'm sending 5 and receiving 96). Could you look through my code to check if I don't miss something obvious?

    Transmitter: https://gist.github.com/anonymous/d81b387e37f67d4abd2f
    Receiver: https://gist.github.com/anonymous/defe28890f49b07cdbf3

    Greetings from Poland!
    Kuba

    ReplyDelete
    Replies
    1. Hi!
      Hard to say, your code looks fine!
      I'm not sure how the transmit-function works when it gets passed an integer instead of an array? or could it be the USART baud-rate, or ASCII that is displaying it wrong?

      Delete
  73. hi Kalle,
    I am from Indonesia. I want to ask you.
    nRF24L01 first time I tried, I do not know if it should use the maximum supply voltage of 3.6 volts.
    I give to the VCC voltage of 5 volts nRF24L01.
    do you think, does nRF240L01 mine can still be used?

    ReplyDelete
    Replies
    1. Hi!
      I guess you just have to try and see if you can get it to work...
      /Kalle

      Delete
  74. Hi...please help...Can you check my codes?

    ReplyDelete
    Replies
    1. sure, if u post them on github and give me the link...

      Delete
  75. This comment has been removed by the author.

    ReplyDelete
  76. Hi, Im thushan. I use arduino as my IDE. this is my problem.
    when Im communicating, I read a digital pin for a external input (using digitalRead()). but some times the program gets stuck due to the serge pulse on the input. now I want to reset the module or the arduino uno to continue the communication.

    I do not know how to redirect the program when the program got stuck during communication.

    can u help me with a code or an idea......?

    ReplyDelete
  77. Hello from Poland,

    would you be so kind and help me with your code? I am strugling with nRF24L01 for some time now with no avail. Here are links for my code:

    Sender (Atmega88)
    https://gist.github.com/spike8888/07591b56a082901e4cef
    Receiver (Atmega32)
    https://gist.github.com/spike8888/b60825b8d56703cad40f

    Thanks in advance

    ReplyDelete
  78. Hi you. You don't have code Atmega8 or 32 ? i need atmega8 or 32 with NRF24L01 :((

    ReplyDelete
  79. how to encode NRF24L01 wireless communication between multiple systems to avoid interference ?

    ReplyDelete
  80. hi, could you please give me example code for atmega16 (master) & atmega8 (slave) to send the display lcd from master to slave with NRF24L01 ?
    thanks :D

    ReplyDelete
  81. Very good tuto...
    I have ten of these modules standing on my desk and some bare ATmega/ATtiny which will happily be used to play with..
    And i like the bare C approach versus the classic arduino lib...

    ReplyDelete
  82. Thanks very much for this. And I agree with the guy above - nice to see not everything has been corrupted by arduino.

    ReplyDelete
  83. This comment has been removed by the author.

    ReplyDelete
  84. Hi Kalle, thanks for writing this great tutorial. I am wondering why you use WriteByteSpi to write "0x70" in your reset method. The comment says it is to reset irq in the status register, but I don't see 0x70 as a valid command in the SPI command table. Where did you come up with that number?

    ReplyDelete
    Replies
    1. 0x70 is 01110000 in binary, and so it functions to clear the 4th, 5th, and 6th bits of the status register (the "irq" bits). See page 56 of the datasheet for more details.

      Delete
  85. This comment has been removed by the author.

    ReplyDelete
  86. How can i use nrf24L01+ as transceiver at a time in above code ? Please tell the solution

    ReplyDelete
    Replies
    1. If you mean transmitter, set config-register bit 0 to "0" (config = 0x1E)

      Delete
    2. I want to use one nrf as transmitter & receiver at a time. So what should I do ?

      Delete
    3. you use the transceiver as a reciever (config=0x1F), until you want to send anything, then you change the config-register to 0x1E, send the package, and then return back to receiving mode (0x1F).

      Delete