Feed on

I decided it was time for me to play around with wireless communication as I recently purchased a wireless alarm system. I was able to pick up two nRF24L01 wireless modules very cheaply at about $2 each from Ebay.

Some features of this chip are:

  • 126 channels
  • Up to 32 bytes payload data
  • Up to 2Mbps
  • About 12mA current consumption when receiving or transmitting

I found there is a RF24 library for the Arduino and a similar library for the AVR. However after looking through nRF24L01 datasheet it appears that the AVR library version appeared a tiny bit incomplete so I’ve made some changes to the code.

The nRF24L01 modules uses 6 data pins but you can get by with only using 5. There are 4 for the SPI, 1 for the chip enable (CE) which sets the nRF24L01 in listen or transmit mode and an interrupt pin (IRQ) which can alert us when a packet arrives, packet is sent or maximum retries reached. I don’t use the interrupt pin in my example.

In my example, I have the transmitting code running on the ATtiny and the receiving code running on the Arduino so I could easily print the results. We run the nRF24L01 from the 3.3V pin on the Arduino.

ATtiny code

Without getting into the specifics just yet, we run the initialise function, delay a few milliseconds for the nRF24L01 chip to start up, run the configuration function to set the channel, payload, addresses, etc and then we have the data to send in an array with the array size (buffer).

After the initialisations are complete, we just keep re-sending the data and pause for 1 second. If the transmission we sent didn’t make it after a few retries, we reset the maximum retries otherwise the nRF24L01 won’t try transmitting any more data.

Arduino code

I’ve skipped the initialising code as it’s the same as in the ATtiny. All we do is check if there is data that has arrived, if not then keep waiting. If there is data waiting for us, then we read the data and print out the results – it all sounds very simple.

Now it’s time to view the code in detail, I’ll be discussing the AVR version.

We just initialise the SPI.

We use the above SPI functions, one that transfers 1 byte and 2 others that write multiple bytes and read multiple bytes.

By looking at the register map we can configure the nRF24L01.

Defines / Settings

We setup RXMODE and TXMODE so we can determine which mode we are in (but it’s not used in my example), define the channel (mirc_CH), the number of bytes to transmit (mirf_PAYLOAD), the configuration to apply and the receiving and transmitting addresses (RADDR and TADDR, 5 bytes maximum). Also we have 2 power up modes we can call to switch between transmit (TX_POWERUP) and receive (RX_POWERUP) modes.

We define the CE and CSN pins plus some defines to make switching between a high and low output easier.

We set up our outputs and turn CE to low and CSN to high. When CE is low it means that the chip isn’t listening for or sending data.

Configuration code

Now we write to the chips registers by using the mirf_config_register function for single byte operations and the mirf_write_register function for multi-byte writes.

We set the RF channel and set the incoming payload length (not really needed for the transmitter).

On page 65 of the PDF it shows what we need to do when receiving and transmitting. Whenever we transmit data, we can set the receiver to auto acknowledge the payload we’ve sent (it’s set on by default) and in order to receive the acknowledgement we need to set the receive address to be the same as the address we transmit to as we’ll receive a packet back.

We’ve set the receive address P0 (pipe 0) and now we just need enable that pipe by setting EN_RXADDR to 1 which we do next. As you can see there are multiple pipes, so it’s possible to have one receiver listen for 6 different addresses.

Lastly we set the chip into transmit mode and we don’t set CE to high just yet.

Config Register / Read & Write Register functions

The config register and read & write register functions are quite easy as it just uses SPI so we just send the above commands and replace AAAAA with the register map address we wish to access. For example, to access the RF_CH register, you would send the address 00000101 and then the byte to write.

Transmitting data

First thing we do is change to transmitter mode and flush any data that may be in the TX buffer from the last time we sent data (this is useful because we may have reached the maximum retry limit which means the data in the TX buffer won’t get cleared).

Next we load our payload and then set the CE pin high for 15us and we are all done, the chip will do the rest. The minimum we need to keep the CE pin high for is 10us.

If we send some data and we don’t receive any acknowledgements back, the MAX_RT is set and we won’t send any more data which is good in a way so we know what’s happened but bad because if the receiver comes back online we try to re-communicate to it. This function will check if MAX_RT is set and early in our code if it was set, we clear it so we can keep trying to send data.

Receiving data

The function above just checks the register to see if there is any data waiting to be read. If there is then we return 1.

When there is data waiting to be read, we can request to read the payload, it will automatically remove the payload from the RX and then we clear the RX_DR bit which alerted us that there was data waiting for us.

Results and Conclusion

Download the code here: nRF24L01_TX-RX_Test_v0.2 (updated with flushing TX/RX)

I have the ATtiny running from a 3V coin cell and the Arduino with a small speaker to beep once some data is received. When transmitting the packet on the ATtiny’s side, it was performed so fast that my cheap multimeter didn’t pick up any current or voltage changes to the coin cell battery I was using. On the Arduino reading side, as it’s set to always be listening it was taking up about 12mA.

Here are the results I received from the serial monitor on the Arduino. As you can see the packet is being received successfully.

We have shown the basic configuration of a one way communication from the ATtiny to the Arduino which you should be able to use for a two way communication. I would recommend reading the datasheet as it provides much more information on how you should interact with the chip as well as flowcharts, examples of the interaction between 2 wireless modules and some tips.

Github page for more examples – https://github.com/insidegadgets/nRF24L01

195 Responses to “Using the nRF24L01 wireless module”

  1. serjio says:

    Did I close the tab without submitting the reply?
    Nice stuff! what’s the expected working range? The distance of 30 meters is okay? How long will my batteries last to send a couple of bytes each minute? Do you plan to use it later in your projects?

    • Alex says:

      I’ve tested 25 metres with line of sight without issues as long as you keep the transmitter stationary so I think 30 metres should be fine. (I’m going to test a longer range in the next week or so)

      If you were to use this in a project, you would want the receiver to have a large battery or be connected to mains as it would always be listening and take up 12mA.

      For the transmitter a rough estimate might be that it transmits the data in 10ms (or less). If you transmit once every 10 seconds and your MCU uses 5uA when sleeping in-between transmitting and you use a 3V coin cell it can last 500 days according to a battery life calculator website (excludes the time the MCU is awake), so you would get at least 1 year.

      I plan to use these with my wireless alarm. The PIR sensors that came with it are battery powered and have a switch to turn them on and off. So I’m hoping I can use these wireless modules so when I turn on the alarm, the PIR’s also turn on and when the alarm is turned off, the PIR’s also turn off – so it will save the battery on the PIR’s. Also am thinking of adding another wireless siren in a different location to where the alarm system is (in case the alarm system is breached and quickly switched off, there will be a redundant siren still going).

      • serjio says:

        well, nice application 🙂 what I am thinking of is a garden weather station: i need a wireless temperature transmitter, so that the river water and soil temperature is logged wirelessly. probably, I ‘d power up the receiver from the wall outlet (or may be from the router – anyway, I wan’t to connect it to the router). hope several transmitters can be used with a single receiver? and i hope they won’t interfere with wifi 🙂
        i need it just for the summer time. however, the design should be ruggedized and waterproof.

        • Alex says:

          Yep you can use several transmitters with a single receiver, example diagram is shown on page 37 of the datasheet PDF. It shouldn’t interfere that much with wifi because it only uses 2mhz of bandwidth but if it does you can always change the channel it uses.

      • Alex says:

        I’ve just tested the range again and got 120 metres (according to Google maps) with line of sight. It could probably go more but I couldn’t travel any further to test.

  2. Krazitchek says:

    Hello, the code don’t compile for me. I have this error:
    “too few arguments to function ‘mirf_write_register’”
    I don’t undestand why !.. i cut/paste the code for the zip file.
    I try to use your code for use with 2 ATmega8. For now i’m just trying to code the TX side et compile it.

    • Alex says:

      Hi, have you tried to compile all the files from the zip instead of cutting/pasting in the code into your own project? Did you include the mirf.c and .h files? If you are on Windows, you can open the project file that was included by using Programmers notepad with WinAVR.

      • Krazitchek says:

        Hi, yes i include all the file from te TX folder of the zip file.
        I’m on Linux (ubuntu) with Eclipse.
        I just modify the SPI part to match SPI of ATmega8. After “reording” some #include, errors disappear. But it doesn’t work yet. No more compilation errors.
        It seems bytes are sent (i flash a led when transmitting) but nothing on RX side !.. i think the problem come from my code not well configured but as i’m a beginner it’s not very easy for me at this point !
        Something i don’t understand in your code in the zip file is in mirf_config function in mirf.c at line “PMODE = TXMODE; // Start in receiving mode”. Is it transmitting or receiving ?.. because of “TXMODE” but with a comment at “receiving mode” !… in the code of the thread it’s written another thing for the same line (PMODE = TXMODE; // Start in transmitting mode)… what is the good one ?!
        I have exactly the same components (nRF24L01) with 2 ATmega8.
        All help are welcome ;-)… thank you for your time and sharing your research. 🙂

        • Alex says:

          Ah, the PMODE is my mistake, I made changes when I was writing this post but didn’t update the mirf.c file. I have re-uploaded the zip file. TXMODE is transmitting and RXMODE is receiving.

          On the transmitter if you add some LED blinks inside the “if (mirf_max_rt_reached()) {” it will tell that it didn’t receive an acknowledgement from the receiver (another way to troubleshoot problems and this is the way I used to test range)

          • Krazitchek says:

            … in the post you wrote:
            mirf_write_register(RX_ADDR_P0, TADDR, 5);
            mirf_write_register(TX_ADDR, TADDR, 5);
            Is it good ?… TADDR for both RX et TX ?

            • Alex says:

              Yes this is needed for the transmitter if you have auto acknowledgement enabled (which is on by default).

              The transmitter sends out the packet to the transmit address, the receiver gets the packet and sends the ACK. The transmitter switches to receiver mode (automatically) to check for the ACK.

              • Ashish Khanal says:

                Hi Alex,
                Thank You for this great tutorial.One thing is so so so confusing me right now.
                I’ll have to explain it in detail,
                I am using nRF as transceiver, and i want to use it as transmitter once and next time as receiver and again as transmitter and so on, for two way communication in home automation.So lets say the data pipes 0 and 1 are enabled by default[from datasheet].Correct me if i’m wrong becoz i’m seeking your suggestions if i make a mistake.
                I’m assuming that data flows in data pipe 1 and acknowledgements flow in data pipe 0.[I also could be wrong here so please correct me]
                The transceiver ‘clnt1’ when configured as TX should have-
                RX_ADDR_P0=clnt1[To receive ack]
                and when it is configured as RX should have-
                RX_ADDR_P1=clnt2[The second transceiver]

                and the second transceiver ‘clnt2’ when configured as RX should have-
                and when it is configured as TX should have-
                RX_ADDR_P0=clnt2[To get ack]

                Should i enable only data pipe 0 to transmit data and acknowledgements between two transceivers like the way you did in your program.
                The way you did was,
                The transceiver ‘clnt1’ configured as TX has-

                and the transceiver ‘clnt2’ configured as RX has-
                Could you please explain me this, coz data sheet confused me that i should write here clnt1 instead of clnt2.
                [Please explain both of above lines bcoz i’m so confused that i cannot think straight.I mean TX_ADDR is the address of transmitter, right.And if you don’t specify this TX_ADDR in Receiver, how will the receiver know the data is from clnt1?]

                [I’m trying to use the link to receive temp. values from the room and use a remote to set a temp. of room to a user input value and see the current temp. value of room in remote and also i could transmit commands to turn fan/heater ON/OFF, so i need two way communication]

                Please help.

  3. Martin says:

    This is a very good find Alex, I use mostly RFM12B for my projects but it is much more expensive, plus power consumption is double.

  4. Kalle says:

    Love your tutorial, but i think i need some help.
    My problem is that i cant even get my Attiny85 (or attiny26) to get a working SPI connection with the nrf…
    The SPI seems to work, but when i try to collect the “CONFIG” or “STATUS” register, the SPI only gets 0xFF.
    I wrote a simple program to do only that, and would be glad if you could have a look at it:

    #define F_CPU 1000000UL // 1 MHz
    void InitSPI(void)
    DDRB |= (1<<PB2); // SPI CLK
    DDRB |= (1<<PB1); // SPI DO (MISO)
    DDRB &= ~(1<<PB0); // SPI DI (MOSI)
    PORTB |= (1<<PB0); // SPI DI

    PORTB |= (1<< 4); //CSN high to start with

    // SPI transfer 1 byte and return the result
    uint8_t spi_transfer(uint8_t data) {
    USIDR = data;
    USISR = _BV(USIOIF); // clear flag

    while ( (USISR & _BV(USIOIF)) == 0 ) {
    USICR = (1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
    return USIDR;

    uint8_t GetReg(void)

    PORTB &= ~(1<<4); //CSN low
    WriteByteSPI(CONFIG); //Telling nrf to read "CONFIG"
    reg = WriteByteSPI(NOP); //Saving "CONFIG" in "reg"
    PORTB |= (1<< 4); //CSN high
    return reg; //

    int main(void)
    DDRB |= (1<<3); //Using PB3 for debug LED
    uint8_t t;
    t=GetReg(CONFIG); //Saves "CONFIG" to "t" —-only gets 0xFF

    if (((t & (1 << 7)) != 0 )) //Test if a bit i know should be "0" is korrect!?
    SETBIT(PORTB, 3); //This tells me i got a 1 where it s
    CLEARBIT(PORTB,3); //turn off LED

    • Kalle says:

      sorry, accedently hited the submit button before i was finished…
      “reg” is declared as:
      “uint8_t reg;”
      in the first line of “GetReg(void)”
      My setup is exactly as yours!

  5. Kalle says:

    and of course. WriteByteSPI() is supposed to be spi_transfer() in the GetReg() funktion…
    got ugly when i copied and pasted from my code to the post 😉

  6. Stephen says:

    I was wondering if its possible to bit-bang 595 shift registers and have the nRF24 connected to the same SPI.
    If so how would the arduino know if there was data to be read?
    Im working on a POV project and some remote control or possibly updating the display from another arduino with a touch screen would be nice.

  7. Alex says:

    Hi, yes you can do that. You would use the slave select pins on each one so they know which one you are talking to. The nrf24 has an IRQ pin for interrupts so you can enable it and it would go high if it received something – you could put a pin change interrupt on the arduino to catch it. Or you could poll it every x milliseconds.

  8. Zhou Feng says:

    It is very interesting. I try to make it with Visual Basic (version number 6) and it doesn’t work at all. I get error in the syntax from line 1 when I try to compile.
    Also. You did not make the UI for this radio with dialog box?

    • Alex says:

      Hi, this won’t compile on anything apart from a compiler that works with Atmel AVR microcontrollers. If you wanted to interface with the nRF24L01 through visual basic – a way to do it would be to have microcontroller to communication with the nRF24L01 and then interface with your visual basic code through the USB.

  9. Rik says:

    I just got an ATMega328, an Arduino UNO, and 2 nRF24L01s. I’ve worked with an Arduino before but I wanted to learn avr programming. I have yet to load any code on the atmega (I tried last night but avrdude kept throwing an error, will try again tonight I think I have the solution).

    I am confused as to what each part of the code you described above is. Are you meant to put all the AVR relevant code into one big long file, compile and upload it as .hex? How do you import/reference the tinkerer libraries and which ones do you need (RF24l01.h, mirf.h, mirf.c)?

    I am doing a project at uni this year, it is a telemetry system for a UAV using an Xmega and Xbees. I want to be able to trasmit sensor data (eg, from a MPU6050 accelerometer/gyro) wirelessly – this is a small scale test using an atmega (with sensor data) and arduino (basestation connected to PC to receive sensor data) – how do you think I should proceed?

    • Alex says:

      For code on this page, you run the receiver on the Arduino (open up the .pde file as normal and it will link the other two files, then upload as normal) and the transmitter is run on the AVR.

      For the AVR side – you should leave all the files as is because they already reference the mirc.c/h files. Using WinAVR with Programmers Notepad make things easier, you compile the TX folder and upload it to your ATMega328 with Programmers Notepad. If you don’t have WinAVR then you’ll need to compile the code using the makefile and upload the .hex yourself.

      If you want to use two AVRs (without Arduino), take a look at this PIR code: http://www.insidegadgets.com/wp-content/uploads/2013/01/PIR_Wifi_Sensor_v0.1.zip

      Since you want to transmit your sensor data to the computer then the Arduino as the reader is the easiest option.

      • Rik says:

        Thanks for replying. Can I just upload the main.hex file?

        How do I compile the whole folder in TX?

      • Rik says:

        Ah I just loaded the project file in. Tried to run ‘make all’ and I get “process_begin: CreateProcess(NULL, /c/WinAVR-20100110/bin/avr-gcc –version, …) failed.
        make (e=2): The system cannot find the file specified.

        make.exe: *** [gccversion] Error 2”

        I also realised that your chip runs is the ATtiny though and mine is an ATMega328p

        • Alex says:

          You can’t just upload the hex file unless you upload to the same chip that I used.

          I must of used an older makefile. If you open your makefile and find “/c/WinAVR-20100110”, then replace it with “c:/WinAVR-20100110” (assuming that’s where your WinAVR folder is) there will be multiple occurrences of this that need to be changed.

          Also if you extracted this project to a folder that has spaces (or any folders above it have spaces) then it won’t compile.

          Since you have the ATmega328p, you’ll have to change the makefile, right near the start you tell it which chip you are compiling for. You will need to check the ATmega328p datasheet to change the pins that I use, because the ATtiny pins that I use in the project won’t be the same for the ATmega328p.

  10. Jero says:

    Hi Alex,

    Thank you very much for your posts! They’ve helped me a lot

    Do you think that it’ll be ok if I connect the Attiny85 directly to a CR2032 without using a capacitor?

    Thanks in advance!

    • Alex says:

      Hi Jero,
      Yes it will be fine to connect the CR2032 without using a capacitor. If you are doing things like using the ADC or using a few mA of current, that’s when the capacitor can help eliminate some noise/small voltage fluctuations.

  11. tom says:

    If i want to use ATtiny25 in RX side coud this code works?

    #include “setup.c”
    #include “mirf.c”

    int main(void) {
    uint8_t buffer[5] = {0, 0, 0, 0, 0};
    //uint8_t buffersize = 5;
    mirf_CE_hi; // Start listening
    while(1) {
    while (!mirf_data_ready())
    {_delay_ms(50);} //necessary??
    And the setup.c, mirf.c, mirf.h, nRF24L01.h could be the same like TX side?
    What is the fuse settings?
    Is _delay_ms(50); necessary? What is the minimum value? I want the data near realtime as possible.
    Do i have to clear/delete data from buffer before receive next 5 bytes?

    • Alex says:

      Hi Tom,

      The fuse settings are the factory defaults. Yes, the code you posted should work, also check out my PIR server code if you need an example.
      Delay of 50ms isn’t necessary, so you can remove it.
      Yes you should clear the data out before you receive the next 5 bytes (to make sure you don’t keep receiving duplicated 5 bytes)

  12. tom says:

    Firs 2 lines is

  13. Matti says:

    Hi, I have a small problem with function mirf_config_register. It sends the command to write register, but it doesn’t send the value. The program stops on line: while(!(SPSR & (1<<SPIF))) in function for SPI 1byte transfer.. Do you know where can be the problem?
    I would be grateful if you could help me.

  14. Alex says:

    Hi Matti,

    I grabbed that code from the ATmega8 PDF. I’ve just tried to simulate the while statement you get stuck on and it does go through it just fine. Could you try to simulate your code?

    Did you set up the outputs/clock too:

     DDRB |= (1<<PB2); // SPI CLK
    DDRB |= (1<<PB4); // SPI DO
    DDRB &= ~(1<<PB3); // SPI DI
    //PORTB |= (1<<PA6); // SPI DI
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);

    Maybe try sticking in “spi_transfer(123);” near the start of your main function to see if it passes that?

    • Matti says:

      Everything is set up, I just can’t find out why it stucks on that line. I think code is correct (based on yours + library for AVR you found + datasheet) because function spi_fransfer pass to nRF24L01 command for writing register ((W_REGISTER 0x20) | (register_mask & register address 0xYZ)), but next step, when it should send the value it waits for transmition complete on line while(!(SPSR & (1<<SPIF))) in the same function spi_fransfer. Can be wrong nRF24L01 chip? Once I accidently supply it with 5V instead 3.3V. Is there any way how to find out if the wireless module is OK?

      • Alex says:

        The nRF24L01 chip should still be ok (I did the same thing – connected it to 5V for a while).
        Can you remove the nRF24L01 module and try without it?
        Are you using a lot of the sram (near maximum)?
        Which MCU are you using?

        • Matti says:

          I don’t use much sram and I use ATMega328P….and it works without the RF module… I still think that damn nRF24L01 chip is wrong..

          • Alex says:

            Hmm, that’s strange. Did you also change the pin definitions in mirf.h? Since it’s an ATmega328 – I have one too, so I might just try this out for myself.

            • Matti says:

              Yes, I did (I have own scheme – CSN-PD5, CE-PD7….more devices on SPI). But now, it is even stranger. It worked for a while without RF module, and now it does the same thing like before (with module and without module too). Damn, why it worked for a while? 😀

              • Alex says:

                I was having problems too but now figured it out. Move “spi_init(); // Initialise SPI” to the end of the mirf_init function and I assume that your CSN pin is the slave select pin – PB2.

                The problem was, we were setting up the SPI before setting the slave select as an output and the datasheet says that causes it to reset the Master SPI bit:

                “This bit selects Master SPI mode when written to one, and Slave SPI mode when written logic
                zero. If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Mas-ter mode

                I saw that happen in the simulation after 2-3 loops.

                • Matti says:

                  Yeaaaah it works :D. I had spi_init at the end of mirf_init function …no, my slave select pin for RF module is PD5, PB2 is slave select for MicroSD, but I haven’t used PB2 yet (Firstly I wanted to run the RF transfer)….so I didn’t have set PB2 as an output. I set it as output in spi_init function and now it works :). It changed MSTR bit in SPCR register to logic zero (slave) and SPIF to logic one (interrupt) as you found in detasheet

                  Thank you very much, you really helped me a lot. Thank you again 🙂

  15. Madd0c says:

    Just an FYI, you may want to put somewhere in the post or the code header that the ATTiny needs to be running WITHOUT the clock divider on, i.e. at 8Mhz. After literally 3 days of tearing my hair out with the demo code and checking wiring about 70 times, I read a post somewhere that the USI wont function for SPI at 1Mhz.

    Just thought it might help someone trying to get this working. Now I can actually start working on my code lol.

    Thanks for the information!

    • Alex says:

      Hi Madd0c,
      That’s odd, I am running my ATtiny85 and 84 at 1 MHz and haven’t had any problems. I’ve checked the datasheet for both and I don’t see any mention of a minimum clock speed.

      Could you advise which ATtiny you are using and link to the post you found?

  16. Madd0c says:

    I am using an ATTiny 85 20PU. I can definitely verify it will not work with the clock divider fuse set. I have tried it on 3 of my chips. Works just fine without the clock divider. I have the oscillator set for 8Mhz internal.

    I ran into the USI/SPI not fast enough at 1Mhz info while digging through a ton of websites with information on interfacing the ATTiny 85 with SPI. It was just a passing mention and not anything from the Datasheet. I don’t know why it works, but I can confirm upping the speed on the MCU fixed my issues. May have something to do with the 20Mhz part?

    Now I am having a ton of issues working with PB5 to use as I/O. Back to the breadboard!
    Thanks again,

    • Alex says:

      I also have the 20MHz part. How long ago did you buy your ATtiny85? What voltage are you running on? Does the ATtiny85 get stuck when it’s running the USI/SPI function? Do you have a logic analyser to check what’s going on? What do your fuse bits read as?

      As you probably know PB5 is reset so it’s a weak IO pin, plus in order to use it you need to turn off the reset functionality of it in the fuses and if you do then you can’t program it anymore unless you use the high voltage method, which is why I don’t use PB5.

      • Madd0c says:

        These are new ATTinys from within the last 2 months from digikey. The only fuse I have set is the SPIE and the 8Mhz int osc. I am using HVSP with a Dragon, so reset disable is not an issue, And I just left the SPIE fuse set, dont really need it. Running on 4.3v Vcc from a LiPo battery source.
        I will try to track down the hangup with the clock divider set, I do have an old logic analyzer I can try to get working on it.

        Thanks again,

        • Madd0c says:

          Tracked down the issue with the 1Mhz setting. It was my own fault, I had 2 seperate hex files I was working with and one had me setting one of the SPI pins as my LED output. I had them labled as 1MHz and 8Mhz, but there was another change other than the #define F_CPU
          Went back to the original and it now is working with both 1Mhz and 8Mhz settings. Also figured out my PB5 issues were because of that bitwise mistake.

          Sorry for the misunderstanding, Thanks for the tutorial, I am slowly getting the SOIC8 ATTiny85 running with the NRF24l01 using the PB5 as an input for my one sensor, thanks to this site.


  17. Vadym says:

    hello, i have an usbasp programmer and i want to know if the spi setup is the same as Attiny85?

    • Alex says:

      I’m not too sure what you mean?
      As long as you copy the way I’ve matched the MISO, MOSI, SCK, CSN, CE pins and change the code to suit the device you are programming then it should work.

  18. David says:


    First of all I must congratulate you for your brilliant work, really well done!
    I have bought two nRF24L01 modules and I am trying to get my ATTiny85 to communicate with my Arduino Mega 2560.

    Here’s what I did:
    1. Uploaded the Arduino as ISP sketch to my Arduino Mega.
    2. Installed WinAVR and programmers Notepad.
    3. Modified the Makefile as such: https://www.dropbox.com/s/azvdregwii0j2i3/Makefile
    4. After much struggling with the Makefile, I managed to get the code to compile and get written to the ATTiny85.
    (C:/WinAVR-20100110/bin/avrdude -p attiny85 -c stk500v1 -P\\.\COM1 -b19200 -U flash:w:main.hex
    avrdude: 716 bytes of flash verified)

    5. Pin layout (Starting from top left pin of NRF (IRQ), where bottom right pin is GND)
    Arduino Mega – nRF24L01
    N/A – IRQ
    50 (MISO) – MISO
    51 (MOSI) – MOSI
    52 (SCK) – SCK
    48 – CSN (Changed in point 6 and point 7).
    53 (SS) – CE
    3.3v – VCC
    GND – GND
    6. I uploaded the “GettingStarted” script from the RF24 library and modified the line with RF24 radio to: RF24 radio(53,48);. Here is a screenshot of the result:
    Since there aren’t a bunch of zeroes in status, I think it may be wired up correctly. I cannot decipher what the numbers mean though, there could be something configured incorrectly.
    7. Modified in mirf.h:
    // Pin definitions for chip select and chip enabled of the MiRF module
    #define CE 53
    #define CSN 48
    Uploaded the RX sketch to the Arduino Mega.
    8. Wired the NRF chip to the ATTiny85:
    ATTiny – nRF24L01
    1 (Reset) – N/A
    2 (PB3) – CE
    3 (PB4) – CSN
    4 (GND) – GND – Arduino GND
    5 (MOSI) – MISO
    6 (MISO)- MOSI
    7 (AI, SCK) -SCK
    8 (VCC) – VCC – Arduino 3.3v
    I have tried swapping around MOSI and MISO with no luck.
    If I add an LED (+) to PB4 in parallel, and ground it properly… I see it blinking every second. I think this indicates that it is at least trying to transmit once per second.

    Now, I make sure that the ATTiny is running and supposedly transmitting, and I look at the serial output from the Arduino Mega. It just shows dots. It never seems to receive anything at all!
    Debugging this is extremely difficult… I have no idea what’s going on and to be honest, this is the very first time I’m using bitwise operators, low level stuff and AVR in general.

    I would really appreciate it if you could give me some hints on what could be wrong.

    • Alex says:

      Hi David,

      Pin layout seems to be ok on the Arduino Mega and on the ATtiny85 (for some reason on the ATtiny chips, it needs to be the MISO to MO and MOSI to MI which you have done).

      If you disconnect MOSI or MISO on the Arduino and restart the RF24 program, do you still receive the output from the Arduino? I’m assuming you are only using the RF24 program for testing the nRF24L01 module and then trying out my Arduino RX program.

      Regarding the LED, it would indicate that something is happening every second.

      Can you try changing:
      #define CE 53
      #define CSN 48
      to different pins on the Arduino? I think the SS pin may be affecting it.

      • David says:

        Thanks for answering.
        If I disconnect MISO and MOSI and try the RF24 program, it gives me output but everything is 0x00.
        I’m using it just to test, I then flush your RX program to the Mega.

        I’ve tried changing pins just now with some success…. when I tried to read the rf setup with MIRF Register before it gave me 0000.
        Now that I’ve changed pins and used:
        #define CE 48
        #define CSN 49
        It gives me: rf_setup = 1111
        Here’s what I’m using to read it:
        byte rf_setup = 0;
        mirf_read_register( RF_SETUP, &rf_setup, sizeof(rf_setup) );
        Serial.print( “rf_setup = ” );
        Serial.println( rf_setup, BIN );
        I place that after mirf_config() in setup().

        However the reciever still does not get anything.
        I’ll try writing a program to turn on an LED if the rfSetup is 1111 on the ATTiny85.

        • Alex says:

          The rf_setup register isn’t important for detecting signals or data, it holds the air data rate, TX power and LNA gain (on the nRF24L01 pdf page 54). But yes, 1111 is normal for rf_setup.

          Can you add this function to my RX code:
          // Read the status register
          uint8_t mirf_status(void) {
          mirf_CSN_lo; // Pull down chip select
          SPI.transfer(R_REGISTER | (REGISTER_MASK & STATUS));
          uint8_t status = SPI.transfer(NOP); // Read status register
          mirf_CSN_hi; // Pull up chip select
          return status;

          Then call this instead of printing the dot:
          Serial.println(mirf_status(), DEC);

          You should get back the number 14 which in binary is 1110. This means the RX FIFO is empty so no packets have been received yet (page 55 of PDF). Let me know what result you get?

          And just confirming that nothing else in my RX/TX mirf.c/.h code apart from pin definitions was changed? (e.g. channel, raddr, tddr, payload, etc)

          • David says:

            I added the code you’ve mentioned to the sketch… here are the actual sketches I’m using – just your code with the pins changed.
            When I run it, I get loads of 14s under each other.
            Maybe I’ve burnt the chip? I’ll try with another NRF24l01 chip but I’m a bit doubtful it’s the problem.

            I’m also uploading some pictures of the circuitry at the moment to that folder, maybe it will help.

            • Alex says:

              Getting 14 all the time just means it hasn’t received any new packets but at least you are getting 14 which means MI/MO/SCK is connected correctly.

              Hmm, on the ATtiny can you switch MISO and MOSI around? (I’m going to update the TX/RX zip file to show this as I say MISO/MOSI instead of MI/MO which is a bit confusing)

              • David says:

                I’ve switched the MOSI with the MISO on the ATTiny a gazilion times 🙂

                I also just finished replacing both NRF chips I was using with two new ones (just in case) still the same problem.

                It’s always just printing out 14. If I swap out the MOSI with MISO on the Mega (not the ATTiny), it will print 0, so I guess it’s good as it is.

  19. David says:

    OK, I’ve burnt the code to the ATTiny85. The LED (wired to the CSN pin) flashes on for a second, off for a second… on for a second, off for a second and keeps going on like that. I will check what the ATMega is spewing out soon, I’ve got to rewire everything each time because I’m also using it as a programmer.

  20. Alex says:

    >Just to verify, doing this on the Mega not the ATTiny… right?
    You should run this on the ATtiny.

    • David says:

      I’ve run the code as instructed and the LED just flashes on and off. Another thing I’ve tried is to “print” a byte from the config register value from the ATTiny.

      I’ve found that with MOSI and MISO correctly, it gives me “10”.
      With them swapped around, it gives me “255”:
      Are there any values from any registers you would like me to read that can indicate something important? I realize it’s hacky, but I’ve got to try everything 🙂

      int main(void) {

      uint8_t buffer[5] = {14, 123, 63, 23, 54};
      uint8_t buffersize = 5;

      uint8_t registerValue = 0;
      uint8_t* registerValuePtr = &registerValue;

      mirf_read_register(CONFIG, registerValuePtr, 1);


      while(1) {
      mirf_send(buffer, buffersize);

      // If maximum retries were reached, reset MAX_RT
      if (mirf_max_rt_reached()) {
      mirf_config_register(STATUS, 1<<MAX_RT);

      void outputDigit(int d)
      for (int i = 0; i < d; i++)

      if (d == 0)
      for (int i = 0; i < 20; i++)


      _delay_ms(3000); //End of digit

      void outputNumber(int number)
      //"Outputs" a number by blinking an LED connected to CSN.
      // The number will be output backwards. Ex: 982:
      // LED Flashes five seconds.
      // LED blinks quickly twice.
      // LED stays on for two seconds.
      // LED blinks quickly 8 times.
      // LED stays on for two seconds.
      // LED blinks quickly 9 times.
      // If the number is zero, the LED will flash extremely quickly for a couple of seconds.

      _delay_ms(5000); //Start of sequence.

      int i = 0;
      do {
      outputDigit(number % 10);
      number /= 10;

      } while (number != 0);

      _delay_ms(5000); //End of sequence.


  21. David says:

    I don’t know what happened, but I rewired everything for what seemed like the 100th time and it worked! Many thanks for your help!
    There’s nothing so satisfying as to see those numbers after four days of tearing my hear 🙂 Thanks!

  22. Stanley says:

    I found yr blog while looking for an attiny85 solution for the nRF24L01+.. Good stuff you have here on the attiny85…

    So far, I managed to get the NRF24L01 running on both the UNO and Raspberry Pi with the fork RF24 libs… https://github.com/stanleyseow/RF24/

    I need to get the attiny85 to work and I read that the attiny85 was not supported in Arduino as it does not support the SPI libs from the UNO/atmega…

    From yr codes, in header file :- mirf.h
    #define RADDR (byte *)”clnt2″
    #define TADDR (byte *)”clnt1″

    How do I make it compatible with my UNO receiver listening for RXADDR/TXADDR ( 0xF0F0F0F0D & 0xF0F0F0F0E1 ) on long long type..

    I tried the codes below but nothing was shown on Serial Monitor… only dots …..

    I scoped the attiny85 on SCK & CSN and see a lot of CLK pulses when the CSN was pulled low… to confirm the codes was running on the attiny85..

    Thanks …

    • Alex says:

      Hi Stanley,
      Try breaking up the 0x number into separate byte parts like this:
      0xF0F0F0F0D becomes
      byte RADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xD};

      • Stanley says:

        Thanks for the reply, let me test it and let you know the results ….

        BTW, without any extra free pins, how can I use the attiny85 to control other I/O ?
        How can I safely use pin1/PB5 for this purpose ??

        • Alex says:

          You could use PB5 (reset) but if you do, you need a programmer that can program in high voltage mode and you need to disable the reset functionality in the fuse bits.

          If you were using it for something simple like turning on an LED, then you could re-use the SCK pin as long as you aren’t using nRF24L01 at the same time.

          Otherwise you could just upgrade to the ATtiny24/44/84.

  23. Fabio says:

    Hi Alex,
    I’m following this post with a lot of interest, as I am trying to build a general purpose / wireless transmitter for home automation. I have an ATTiny85 + NRF2401l on one end, and an Arduino Uno + another NRF module on the other.
    I am trying to get the thing work on the ATTiny side and it seems I’m facing similar issues as David.
    Actually I am using a different NRF24l01 code for the Attiny, as I am using my Arduino as an AVR programmer.
    Is there a way I can use your TX code with the Arduino environment (using arduino as a programmer for my attiny) ?

    • Alex says:

      Hi Fabio,
      I assume you mean that you are running the Arduino environment on the ATtiny.

      I’ve modified the code which should run on the ATtiny25/45/85 using the Arduino environment (haven’t tested it though): http://www.insidegadgets.com/wp-content/uploads/2013/04/nRF24L01_TX_Test_ATtiny_Arduino.zip

      • Stanley says:

        Hi, I’m also using this Arduino IDE code for attiny85 as it is easier for me to edit and upload the hex using a USBtinyASP …

        I still can’t get it running yet…. with the supplied Arduino mirf or the RF24 libs…

        Can I confirm a few things for this… as I am using the RF24 libs on the Arduino and there are a few minor differences…

        Is the CRC length 8 or 16 ? I think you are using 8bit CRC length, right ?

        How do I turn on DynamicPayload on the mirf libs ?

        Can you help me to put the TADDR and RADDR address as :-
        0xF0F0F0F0E2 instead of (byte *) “serv1”

        I scope the attiny85 and can detect CSN and CLK but totally no voltage on MISO or MOSI pins…

        On yr pinmap, you put pin6/PB1 = MOSI and pin5/PB0 = MISO.. from the attiny85 Datasheet, the pinout is PB0 = MOSI and PB1 = MISO..

        Are these two related or you just configure them differently for the nRF ??


        • Alex says:

          Hi Stanley,

          The CRC is set to 1 byte (by default).

          For DyanmicPayload, I haven’t really looked into it but the datasheet says: In order to enable DPL the EN_DPL bit in the FEATURE register must be set. In RX mode the DYNPD register has to be set. A PTX that transmits to a PRX with DPL enabled must have the DPL_P0 bit in DYNPD set. The MCU can read the length of the received payload by using the R_RX_PL_WID command.

          So you would use write register command to write to the DYNPD and FEATURE register.

          For TADDR and ADDR use:
          byte TADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xE2};
          byte RADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xE2};

          For the ATtiny pin out it’s:
          PB1 goes to nRF24L01 MO label
          PB0 goes to nRF24L01 MI label
          (for the Arduino/ATmega devices, for some reason it’s the opposite)

          • Stanley says:

            For the attiny85, since I was using the Arduino IDE instead of the editor and makefile, I found this when I scope the MOSI & SCK.. I can “see” tiny pulses but they were like 200mV instead of 5V pulses.. first thing that came to my mind was … DDRB was not set to output whereas there are pinMode for CE & CSN in the mirf_init()..

            I copied the AVR setup.c spi_init() lines below the pinMode() for CSN & CE…
            DDRB |= (1<<PB2); // SPI CLK
            DDRB |= (1<<PB1); // SPI DO
            DDRB &= ~(1<<PB0); // SPI DI
            PORTB |= (1<<PB0); // SPI DI
            and when I re-flash the attiny85 again.. now I'm seeing the MOSI/SCK pulses as 5V…

            I notice that besides the above statements I added, there are totally no references to PB0, PB1 & PB2 anywhere in the codes especially for the SPI stuff…

            Where in the code is the timer/DIVIDER/speed set for the SPI…

            When I compiled the attiny85, I put it was internal 8Mhz…

            To prevent conflict of different mirf libs, I had to rename the mirf.h to attiny_mirf.h and nRF24L01.h to attiny_nRF24L01.h or else it "might" use other libs that was installed in the Arduino IDE..

            I think I'm getting closer to making it work…

            Again, thank you for yr patience to reply all my questions…

            • Alex says:

              For the PB2, PB1, PB0 – it’s just to initialise SPI and the spi_transfer function does the rest for us in hardware.

              For the SPI speed, Clock MHz / 2 which is the fastest it will go. At the moment it uses the 4 bit clock which at 1MHz clock, you get 1 clock high and low of the SPI per 8uS. You can change it to be the fastest (clk/2) by checking page 114 of the PDF for an example. To adjust the speed (if you don’t want the fastest but somewhere in-between) you can use timer0 compare match shown on page 121.

              If you are changing the clock MHz, remember to change the fuse bits too to reflect this.

  24. Mark says:

    Hello, what modification I have to do to use your example in a ATTINY44? only change MOSI MISO SCK pins for ATTINY44?

    • Alex says:

      Hi Mark, that’s correct – you need to change the MOSI, MISO and SCK to the correct pins in setup.c and also change the CE and CSN pins in mirf.h (and if using different ports change for CE/CSN change that too in the mirf_init function in mirf.c).

      • Mark says:

        I made the following changes:


        spi_init void (void) {
        DDRA | = (1 << PA4) / / SPI CLK
        DDRA | = (1 << PA6) / / SPI DO
        DDRA & = ~ (1 << PA5) / / SPI DI
        PORTA | = (1 << PA5) / / SPI DI


        / / Pin definitions for chip select and chip enabled MiRF of the module
        # define CE PA3
        # define CSN PA2

        / / Definitions for Selecting and enabling module MiRF
        # define mirf_CSN_hi PORTA | = (1 << CSN);
        # define mirf_CSN_lo PORTA & = ~ (1 << CSN);
        # define mirf_CE_hi PORTA | = (1 << CE);
        # define mirf_CE_lo PORTA & = ~ (1 << CE);


        mirf_init void (void) {
        / / Define CSN and CE as Output and Set Them to default
        DDRA | = ((1 << CSN) | (1 << EC));

        Is this correct?

        • Alex says:

          If you swap PA5 and PA6 in setup.c, it should be all good:

          DDRA |= (1<<PA4); // SPI CLK
          DDRA |= (1<<PA5); // SPI DO
          DDRA &= ~(1<<PA6); // SPI DI
          PORTA |= (1<<PA6); // SPI DI
  25. Stanley says:

    Dear Alex,

    I’ve finally got it running… Thanks for yr help, really appreciate all the guidance from you… running/debugging without serial monitor or a Logic Analyzer is really running blind…

    Details are here :-

    I made some changes to use 1Mbps instead and use someone else SPI85 and mirf85 libs for Arduino IDE…

  26. Kihon says:

    Hi Alex,

    Can you set the CE permanently hi/low if you are only interested in transmitting/receiving? (allowing you to free up one of the io pins?)



    • Alex says:

      Hi Kihon,
      You set CE high when you want to listen and CE low when you want to stop.
      For transmitting it’s different, you should only set CE high for 15us and then back low again.
      You could potentially use the other pins as long as CE is low and CSN is high.

  27. Kihon says:

    As an additional question, Is it possible to disable the transceiver periodically, and reuse pins for something else?

  28. Stanley says:

    I compiled an Arduino attiny85 repo at https://github.com/stanleyseow/attiny-nRF24L01
    This repo is using modified mirf85 and SPI85 libraries..

  29. Brent says:

    Hello Alex,
    I am trying this for the ATMEGA328P, & I have a question.
    I’m not understanding your mirf_config(void) function..

    // Set RADDR and TADDR as the transmit address since we also enable auto acknowledgement
    mirf_write_register(RX_ADDR_P0, TADDR, 5);
    mirf_write_register(TX_ADDR, TADDR, 5);

    Here, what is TADDR? The address of the transmitter, correct?
    In your header file, you have that defined as:
    #define TADDR (byte *)”clnt1″

    Not familiar with the syntax & I was wondering if you could explain what is going on with this?


    • Alex says:

      Hi Brent,
      T_ADDR is the address of the other wifi device.
      The TX code has an receive address as clnt2 and transmits to clnt1.
      The RX code has a receive address as clnt1 and transmits to clnt2.

      In this example the TX transmits and the RX receives without sending an acknowledgement back to the TX.

      mirf_write_register(RX_ADDR_P0, TADDR, 5); — I shouldn’t really have this line in the TX code as it’s for receiving information back which we don’t do in this example.

      If you want another example, check out this PIR example in which the receiver sends back an auto acknowledgement – http://www.insidegadgets.com/wp-content/uploads/2013/01/PIR_Wifi_Sensor_v0.1.zip

      • Brent says:

        I saw that you were recently discussing this project using the 328P.
        Do you still have an example using that chip?

          • Brent says:

            The only thing that I’m still not understanding is the defining of RADDR & TADDR.
            I understand that (byte *)”pir1c” & “”pir1s” are the receiver & sender addresses, but I don’t get what to set them as.
            Do I just replace “pir1c” & “pir1s” with a variable that is defined as a static address of my choice?
            Like, say I wanted my receiver’s address to be.. 0xAAAAAAAAAA.
            I chose that arbitrarily.
            How would I go about that?

            • Brent says:

              And also, I read the comments prior to mine & saw that you recommended to define as
              byte TADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xE2};
              byte RADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xE2};

              That gave me the errors:
              “byte” redefined
              ‘TADDR’ undeclared (first use in this function)
              ‘RADDR’ undeclared (first use in this function)

              • Alex says:

                TADDR and RADDR can be anything you like them to be just as long as they match each other on either end, at the moment we have the address in the nRF as being 5 bytes long. Although I will mention that if you make it all AAAAAAAAAA (all the same values), it can make it more prone to noise.

                The line about byte TADDR[] = {0xF0, 0xF0, 0xF0, 0xF0, 0xE2}; is useful if you use the mirf nRF code on the Arduino which someone else produced so you can make the TADDR/RADDR the same format as they have.

                If you are using 2x 328Ps then you can continue to use (byte *)”pir1c” – giving each address a name like pir1c or clnt1 gives it an easy name for us to use.

                • Brent says:

                  Thanks. I understand I just didn’t get the syntax of

                  (byte *)”pir1c”

                  Like how it works. So pir1c is a variable, that you store a 5-byte address in. And the (byte *) will.. be a pointer to a byte of that address?

                  • Alex says:

                    Yes it’s a pointer.

                    TADDR is the variable and the pointer says treat the text pir1c as byte (or in other words as uint8_t).

                    You would look up the ascii table for p which gives 0x70 which is what TADDR would see for the first byte.

                    • Ashish Khanal says:

                      Hi Alex,
                      I checked the code of your PIR WiFi Sensor and understood meaning of TADDR and RADDR.But what i didn’t understand was why did you selected data pipe 0 and 1 and would you please tell me the meaning of RX_ADDR_P1=pir1c in TX code.

                      And what technique should i use to make TX side run as both TX and RX simulataneously and also the same for the RX side.
                      Thank You.

  30. Jony Sinambela says:

    Hi Alex, would you like to give me your email? so we can share about this nrf, coz i’m a newbie.

    best regard

    jony sinambela

  31. jomjom says:

    any ideas on how to synchronize the input when using a shift-register for the outputs of the nRF?

    • Alex says:

      Hi Jomjom,
      Could you explain what you’d like to achieve with the shift register and how you have it configured?

      • jomjom says:

        Hi Alex,
        I want to gain some extra pins so I was thinking to use a shift register for the output pins of the attiny. So 6 of the pins of the nrf will correspond to 3 pins on the attiny.
        Does this makes any sense?

        • Alex says:

          Hi Jomjom,
          Yes that makes sense and will work for transmitting things to the nRF however you won’t be able to receive anything back from the nRF module unless you use 1 more pin on the ATtiny for the nRF MISO and 1 more pin if you want to use the nRF IRQ.

          • jomjom says:

            Hi again Alex,
            that’s the issue, I’m not sure how to synchronize the shift register signals with the nRF MISO signal.

            If I wanted to use the attiny+nrf only to send some info, nRF MISO should be always LOW (grounded), right?

            Thanks for responding; thanks for your much useful code and tutorial.

            • Alex says:

              You should leave MISO floating otherwise it’ll draw 50-100 microamps. Are you just using a 74HC595?
              If you wanted to sync the MISO you would have to read that pin when you set the clock transitions from low to high.

              It may be possible to connect the MISO to the SPI data out with a resistor so you could quickly switch the SPI data out pin into an input to read the MISO bit and then switch back, but really you would need a logic analyser or similar to test/experiment this theory.

              • jomjom says:

                Yes, just a 595. I’ll try to see if I can get it working. Thanks for the info.

                For some reason, by the way, I can’t get a signal when I disconnect MISO from the attiny.

  32. Damien says:

    Hello Alex,

    According your convesation with jomjom, may I re-ask what Stephen asked earlyer.

    Do you think it would be possible to bypass the usage of an intermediate atiny and dirrectly bit bang a 74HC595 from a nRF properly configured ?


    • Alex says:

      Hi Damien,

      Yes you can bit bang a 74HC595 connected to the nRF but you would only be able to send data to it and you could have it send data to other nRFs but you can’t read any data from it like incoming packets.

      (I may have misunderstood your question and if so, could you draw a diagram of the setup you want to achieve with the direction of data transfer?)

      • Damien says:

        (Sorry for the late answer)
        I think you get it right, my idea is pure one way communication 1 to N.

        One master with message destination address changes when needed and some (N) preconfigured nRF directly plugged to some 595 without any ‘smart part’.
        At the first time I think of using IRQ on the receiver side for latching (ST_CP?) the 595, is that a good path ?

        I’ve just received my nRF, I’ll be able to take a closer look on this 🙂

        Thanks for taking time to answer to my noob questions, if you don’t mind dumber question might come 😀

        • Alex says:

          > some (N) preconfigured nRF directly plugged to some 595 without
          > any ‘smart part’

          You won’t be able to plug in the nRF to the 595 without using a MCU to control the 595. If you need more pins on your ATtiny25/45/85 (I’m assuming you are using these), I would upgrade to the ATtiny24/44/84 and then you won’t need the 595.

  33. Ashish Khanal says:

    Hi Alex, i went through the address configurations that you did in your PRI WiFi Sensor program and i got the following,
    In TX program,
    I understood that TX_ADDR contains the address of receiving node.So that’s clear to me.But would you please explain how RX_ADDR_P0 and RX_ADDR_P1 work.
    Does this mean that this TX program configured addresses in a way that the nRF will work both as transmitter and receiver if we program it appropriately to send and receive data.

  34. Ashish Khanal says:

    Hi Alex,
    When both data pipes 0 and 1 are activated, from which datapipe the receiver will read the data?
    [From the address configurations in your PRI WiFi sensor]
    The name of the TX is pir1c and the name of the RX is pir1s and the address configurations for the TX is,

    Please explain the literal meaning of RX_ADDR_P0 and RX_ADDR_P1.How the acknowledgements are received and how the packets are received and where they are received[pipe 0 or pipe 1].
    And please suggest me how to use both the transceivers simultaneously to send and receive data, how should i configure their addresses and at which part of the program should i do it?

    And would you please explain the excerpt from the datasheet in relation to the program in RX of the PRI WiFi sensor,
    “To ensure that the ACK packet from the PRX is transmitted to the correct PTX, the PRX takes the data pipe address where it received the packet and uses it as the TX address when transmitting the ACK packet.”

    Address configurations in the RX program are-

    Which address here is used to send the ack packet.TX_ADDR?
    And what is the meaning of RX_ADDR_P0 and RX_ADDR_P1

    Thank You.

    • Alex says:

      Hi Ashish,

      You don’t have to have auto acknowledgements enabled, it’s just a nice feature to have so you know if your packet was received by the recipient.

      RX_ADDR_P0 is the data pipe 0, it is responsible for receiving the auto acknowledge from the other nRF, it can also be used as a normal receiving pipe too if need be. RX_ADDR_P1 is just a normal receiving pipe. The nRF packets that are send has no field to say who it’s from, it only has a to address, this is why RX_ADDR_P0 needs to be same as the TX_ADDR, because the other nRF that replies can only send a packet back to the address it received it on.

      On the server (RX) it has TX_ADDR & RX_ADDR_P0 = pir1c, RX_ADDR_P1 = pir1s
      On the client (TX) it has TX_ADDR & RX_ADDR_P0 = pir1s, RX_ADDR_P1 = pir1c

      The way it all works is like this:
      Server sends a packet from TX_ADDR (pir1c) to the client
      Client receives the packet on RX_ADDR_P1 (pir1c), since auto acknowledge is enabled it automatically sends a reply back using the data pipe that it arrived – RX_ADDR_P1 (pir1c). So it transmits a packet to pir1c.
      The server matches the incoming ACK packet to pir1c on RX_ADDR_P0
      (Since we have RX_ADDR_P0 set up on the client too, the transaction above can occur from the client to server)

      To use both the transceivers simultaneously to send and receive data, just use the example above, it covers auto ack from either end.

      “To ensure that the ACK packet from the PRX is transmitted to the correct PTX, the PRX takes the data pipe address where it received the packet and uses it as the TX address when transmitting the ACK packet.” – Since there is no “From address” in the packets that nRF’s send each other, there is no way a nRF can know who the packet came from. So it sends the packet to the pipe address that it received the packet on which is why on the other nRF, RX_ADDR_P0 needs to match TX_ADDR.

      > Address configurations in the RX program are-
      > TX_ADDR=pir1c
      > RX_ADDR_P0=pir1c
      > RX_ADDR_P1=pir1s
      > Which address here is used to send the ack packet.TX_ADDR?
      It’s taken from RX_ADDR_P0

      > And what is the meaning of RX_ADDR_P0 and RX_ADDR_P1
      Explained at the start of my post

      • Ashish Khanal says:

        Hi Alex,
        Thank You very much for the prompt reply and i understood many details of nRF by reading your post.So i want to test my understanding and please correct me if i’m wrong,
        Previously i was confused by imagining that when two transceivers are set up for two way communication, their data pipes are mapped one to one[eg.Data pipe 0 of nRF#1 virtually connected with Data pipe 0 of nRF#2 and so on].But now i see that, that was all wrong.
        What i interpret about data pipes now is,
        -Data pipe 0-It’s like a bucket that holds the address of the other transceiver who is supposed to transmit acknowledgements to it because we just sent something to this other transceiver.
        -Data pipe[1,2..5]-They are also like buckets which hold the address of their own transceiver so if they find packets addressed to this address, they read the packet into RX BUFFER.

        Anyway Alex, Thank You again for this great tutorial and your concept clearing posts.

        • Alex says:

          Hi Ashish,

          Yep, that’s correct.
          (Also if you disable auto acknowledgement, then data pipe 0 just can just act as a data pipe [1 – 5].)

  35. Ashish Khanal says:

    Hi Alex,
    I want to ask you another question about auto-acknowledgements and Maximum Retries,
    Taking your code as reference and modifying a little bit,

    uint8_t buffer1[5] = {14, 123, 63, 23, 54};
    uint8_t buffer2[5] = {16, 67, 78, 49, 50};
    uint8_t buffer3[5] = {31, 33, 76, 98, 51};
    uint8_t buffersize = 5;
    while(1) {
    // If maximum retries were reached, reset MAX_RT
    if (mirf_max_rt_reached()) {
    mirf_config_register(STATUS, 1<Will buffer2 be overwritten by buffer3?
    >Will buffer3 be lost when nRF retransmits older packets?

    -If program flow back in MCU has reached to the line for sending buffer3 to nRF[at this point of time we’ve cleared MAX_RT for sending fail of buffer2], will the nRF simply flush[Your program flushes TX FIFO before sending new packet]the existing buffer2 in TX FIFO and send buffer3 now so that the existence of buffer2 disappears in this particular loop of the program?

    Thank You

  36. Ashish Khanal says:

    [Sorry that the previous post was incomplete]
    Hi Alex,
    I want to ask you another question about auto-acknowledgements and Maximum Retries,
    Taking your code as reference and modifying a little bit,

    uint8_t buffer1[5] = {14, 123, 63, 23, 54};
    uint8_t buffer2[5] = {16, 67, 78, 49, 50};
    uint8_t buffer3[5] = {31, 33, 76, 98, 51};
    uint8_t buffersize = 5;
    while(1) {
    // If maximum retries were reached, reset MAX_RT
    if (mirf_max_rt_reached()) {
    mirf_config_register(STATUS, 1<Will buffer2 be overwritten by buffer3?
    >Will buffer3 be lost when nRF retransmits older packets?

    -If program flow back in MCU has reached to the line for sending buffer3 to nRF[at this point of time we’ve cleared MAX_RT for sending fail of buffer2], will the nRF simply flush[Your program flushes TX FIFO before sending new packet]the existing buffer2 in TX FIFO and send buffer3 now so that the existence of buffer2 disappears in this particular loop of the program?

    Thank You

  37. Ashish Khanal says:

    [Sorry again,I don’t know why i’m unable to post the question entirely as the middle part of this post is filtered out after hitting the submit button.So i’m deleting the program part and see if it works]

    Hi Alex,
    I want to ask you a question about auto-acknowledgements and Maximum Retries,
    Taking your code as reference and modifying a little bit,
    The MCU simply puts each array inside the TX FIFO and it’s all nRF afterwards, right.

    1) So if we put array continuously, won’t the new data coming after the old one put in TX FIFO overwrite this old one when the old one is not transmitted completely?[Is that why you’ve put delay of 1s in your original program].

    2)Say if the nRF is currently transmitting buffer2 and failed many times that it’s MAX_RT is set.If we don’t clear it, what happens?Will it stop making any transmits and stop at point where it reached the limit?Will SPI continue to overwrite the TX FIFO or nRF won’t accept any input from MCU?

    -If MAX_RT is reached,nRF won’t clear the TX FIFO buffer,right.
    -Say when nRF is transmitting buffer2 and failing many times to get ACK it will retransmit buffer2 at which point of time lets say the program in MCU sends buffer3 into the nRF[MAX_RT not set yet at this point].
    >Will buffer2 be overwritten by buffer3?
    >Will buffer3 be lost when nRF retransmits older packets?

    -If program flow back in MCU has reached to the line for sending buffer3 to nRF[at this point of time we’ve cleared MAX_RT for sending fail of buffer2], will the nRF simply flush[Your program flushes TX FIFO before sending new packet]the existing buffer2 in TX FIFO and send buffer3 now so that the existence of buffer2 disappears in this particular loop of the program?

    Thank You

    • Alex says:

      Hi Ashish,

      If you are posting code, sometimes wordpress filters it out so if you have lots of code, best to put it on pastie.org.

      1. The FIFO allows for 3 RX data and 3 TX data. In this case, all 3 buffers will stay in the FIFO if the first one was not transmitted successfully. If you have say 4 buffers, I think that buffer4 would not be accepted (I’m not 100% sure).

      2. If MAX_RT is set, then it will not transmit any more data at all. It should still accept TX FIFO data though until it is full.

      >If MAX_RT is reached,nRF won’t clear the TX FIFO buffer,right.
      Yes it won’t clear it.

      >Say when nRF is transmitting buffer2 and failing many times to get ACK it will retransmit buffer2 at which point of time lets say the >program in MCU sends buffer3 into the nRF[MAX_RT not set yet at this point].
      >Will buffer2 be overwritten by buffer3?
      No, it will just be added to the FIFO presuming that there there is enough space left.

      >Will buffer3 be lost when nRF retransmits older packets?
      The nRF will stop at the MAX_RT packet and won’t advance forward to the next FIFO unless MAX_RT is cleared and if it then successfully transfers the existing buffer2.

      >If program flow back in MCU has reached to the line for sending buffer3 to nRF[at this point of time we’ve cleared MAX_RT for sending fail >of buffer2], will the nRF simply flush[Your program flushes TX FIFO before sending new packet]the existing buffer2 in TX FIFO and send >buffer3 now so that the existence of buffer2 disappears in this particular loop of the program?
      If MAX_RT is cleared, then nRF will try to re-transmit the packet that caused the MAX_RT.
      I just flush the TX FIFO because I don’t want to have a packet that the nRF keeps trying to re-transmit without any luck.
      buffer2 won’t be cleared unless you flush TX FIFO but then if you do flush it, if you transferred buffer3 to the FIFO then you will lose that too.
      I try to only load 1 packet in the FIFO which is why I can flush it without any concerns for other packets in the FIFO.

  38. Mangesh K says:

    Hi Alex,
    You done really good job. I like this post and really very very appriciate your help.
    after reading this post I placed order for 2 tranceiver on ebay. I will try to use as cable replacement. I tried searching for this information (about nrf24) long time and at last I got from your post.
    Please let me know where I can get latest code. I want to modify this code to work on 89c51 (software spi). can you please share any code to work on Atmel 89c51.
    Thank you very much for valuable information.

    • Alex says:

      Hi Mangesh,

      The latest is this file: http://www.insidegadgets.com/wp-content/uploads/2013/08/nRF24L01_RX_TX.zip
      You just have to change the pins to suit your AVR and make sure you set the SPI function up correctly depending on your AVR.

      • Mangesh says:

        Thanks Alex, It is really helpful. I made some changes to work on 8051.
        One questions : My observation is there is hugh data loss and communication is not realiable. For example if I transmit a packet for 300-400 times then one packet may receied at RX. I am not sure if others facing the same. but please suggest if there is any solution. Curretnly I am using 8 byte length buffer, and distance between RX & TX is just 1 ft.

        • Alex says:

          Hi Mangesh,
          That certainly isn’t normal and I haven’t experienced anything as bad as that. When you transmit the packet can you check the STATUS register in the nRF to see what it shows, it could be that the maximum retries is set. How about flushing TX/RX before every transmit? Also check the STATUS register on the RX side. Maybe try a different channel as there could be interference?

  39. Ashish Khanal says:

    Hi Alex,
    I’m here again to Thank You for your valuable help regarding nRF.Today i demonstrated final year project of ECE to control lights,fan etc. in a room using nRF that uses two way communication to transmit command to control the actuators and display their status in a LCD display.
    I’m really happy to get it working with your help and came here to Thank You very much!

  40. jomjom says:

    Hi again,

    when I remove _delay_ms(1000); from the transmitters’ code, the receiver doesn’t receive anything. I found that a delay of some μs is needed.
    Why is that? As far as I understand, a stream of mirf_send()s should be OK.


    • Alex says:

      Hi Jomjom,

      I think it could be the flush TX/RX that could be executed right after the packet is queued up for tranmission, this could potentially wipe the data in the TX buffer. Try commenting out the flush TX/RX and see how you go.

  41. Jose says:

    Hi. I have a simple question: can the same NRF24 module act as sender AND receiver when connected to an Arduino? I’m trying to figure that out, but most examples are based on an one-way configuration (sender -> receiver). I need to implement a bi-directional communication between two Arduinos. Thank you!

    • Alex says:

      Hi Jose,

      Yep, the same NRF24 module can act as a sender and receiver. If you leave both as waiting to receive data, then either of them can send each other data.

      You just need to set them up the same way, like the below on both sides in the mirf.c file:

      // Set length of incoming payload 
      	mirf_config_register(RX_PW_P0, mirf_PAYLOAD);
      	mirf_config_register(RX_PW_P1, mirf_PAYLOAD);
      	// Set auto retransmit delay to 500uS and leave auto retransmit count to 3
      	mirf_config_register(SETUP_RETR, (1<<4) | 3);
      	// Set RADDR and TADDR
      	mirf_write_register(RX_ADDR_P0, TADDR, 5);
      	mirf_write_register(RX_ADDR_P1, RADDR, 5);
      	mirf_write_register(TX_ADDR, TADDR, 5);
      	// Enable RX_ADDR_P0 and RX_ADDR_P1 address matching since we also enable auto acknowledgement
      	mirf_config_register(EN_RXADDR, (1<<ERX_P0 | 1<<ERX_P1));
  42. Mangesh says:

    Hi Alex,
    One Question I have.
    I gone through your code and found below in (nRF24L01_RX v1)

    file : mirf.c of RX code
    // Set auto retransmit delay to 500uS and leave auto retransmit count to 3
    mirf_config_register(SETUP_RETR, (1<<4) | 3);

    Please give me idea how it will work in RX code I am confused becuase I guess this should be work in TX mode.

    • Alex says:

      Auto re-transmit happens automatically by the nRF itself. If you have it setup as above, it will retransmit the packet if it hasn’t received a reply back in 500uS and it will do this 3 times. The microcontroller can do other things whilst it waits for a reply back or you can just keep checking the STATUS register to see if TX_DS is set to show it transmitted and got a reply back.

      • Mangesh says:

        Thanks for both your reply.
        I am sorry for confusion I mean I found this code in receiver code. But This step is missing in mirf.c of Transmitter code. is this setting is to do at TX side of RX side?


        • Mangesh says:

          sorry typo. where we need to configure SETUP_RETR ?
          in RX side ? or TX side ?

        • Alex says:

          I think I had a mistake in the original code, it should be in the TX side if there is only a one way communication to RX. If there will be two way communication, then it should be in both RX and TX. (But for simple packets, it doesn’t matter if it’s not there because the nRF sets up the Auto Retransmit Count as 3 by default with 250uS delay)

  43. […] that have tinkered with the nRF24L01, I found more informative posts from 2012 – one on the  InsideGadgets blog the other found on Electodragon – nodded towards the Mirf library.  Given the age of these […]

  44. Stefan says:


    Thanks for the post; I’ve been looking for something exactly like this, as I am trying to redesign a sensor network using nRF24s instead of xBees. I need to replace the Arduino part with something else, but to I need to make sure the ATTiny part works first.

    I’m new to the microcontroller stuff. What would you advise as development environment (I’ve been using plain command line so far, and various ATTinys, but I guess something more “modern” wouldn’t hurt) ?

    Thanks again.

    • Alex says:

      Hi Stefan,

      I personally just use WinAVR with Programmers Notepad for the text editor which can build/clean/program with shortcut keys, it has everything I need (http://www.insidegadgets.com/2011/04/30/move-from-arduino-software-to-winavr-with-programmers-notepad/).

      If you want more functionality and features, you could go with AVRStudio.

      • Stefan says:

        Thanks. I’ll give it try.

      • Stefan says:


        I’ve tried the AVRStudio 4 and it looks ok. However, my setup does not work, and I’m a little bit lost, since I don’t have much experience in this field, and I don’t have a clue about how to really debug.

        Here’s what I did : I’m using an ATTiny45 (my plan is to use a ATTiny861 in the end, but I wanted to make sure I get everything right before that), which gets programmed ok, and I can see on the scope the CSN going low every second and the SCK, too.

        On the arduino side, I’ve used a Leonardo board, since it’s what I have. The code compiles and gets uploaded ok, and the serial monitor shows it’s running.

        But that’s about it; no data seems to be received on the arduino end. The problem is I have no idea how to test which end is not working like it should, tx, rx, or maybe both, so any hint will much appreciated.

        The nRF24 module are these : http://www.seeedstudio.com/depot/nrf24l01module-p-1394.html

        Thanks in advance.
        Best regards,

        • Alex says:

          Hi Stefan,

          On the Arduino side, see if you can print the STATUS register by doing:

          Serial.println(mirf_status(), DEC);
          uint8_t mirf_status(void) {
            mirf_CSN_lo; // Pull down chip select
            SPI.transfer(R_REGISTER | (REGISTER_MASK & STATUS));
            uint8_t status = SPI.transfer(NOP); // Read status register
            mirf_CSN_hi; // Pull up chip select
            return status;

          If your transmitter and receiver are on the same channel and each others address, then you should see the status register output change. You can check what each bit means on the nRF24 PDF page 59 – http://www.nordicsemi.com/chi/content/download/2726/34069/file/nRF24L01P_Product_Specification_1_0.pdf. Check RX_DR, RX_P_NO and MAX_RT.

          You can do the same the ATtiny TX side by having an LED blink when TX_DS is 1.
          Also try flushing the RX and TX before sending.

          • Stefan says:


            Digging a little bit more, and trying to figure out if the nRF is communicating with the arduino board, I discovered that Leonardo’s SPI pins are only available on the ICSP header. SO I need to work some things out better.

            Thanks again. I’ll let you know how it goes.

          • Stefan says:

            Well, that’s what’s happening when you don’t know what you’re doing. After rechecking everything and hooking up the modules the right way, everything works as expected, of course.

            Even changed the ATTiny45 with a ATTiny861, and still got the right behavior.

            Now, I’m off to use the nRFs as a way of debbuging and sending commands to the ATTiny 😉

            So I’ll have to see how I can do a two-way dialog; from the arduino to the ATTiny and back.

            Thanks again for your help and the tutorial. They just proved invaluable to me.

            One last thing… do you have any idea if the nRF24s are capable of “talking” to a ATMega128RFA1 ?

            Best regards.

            • Alex says:

              Good to hear it’s all good.

              That ATmega chip looks interesting but pricey, at a first glance I would say it may not be compatible –
              High performance RF-CMOS 2.4 GHz radio transceiver targeted for IEEE 802.15.4™, ZigBee™, IPv6 / 6LoWPAN, RF4CE, SP100, WirelessHART™ and ISM applications

              • Stefan says:

                Back again… I’m trying to do a two-way communication between the two modules, but I don’t seem to get it to work.

                If this is ok with you, we can “talk” via email, as to keep this page somehow cleaner. My email is stefanu123 -at- gmail -dot- com. I could explain in more depth what I am trying to do, and maybe you can guide me a little more.

                Thanks again. Best regards.

  45. Mantico says:

    Hi Alex,
    Do you have a sketch that does the bidirectional communication with auto ack?

    I think I had a mistake in the original code, it should be in the TX side if there is only a one way communication to RX. If there will be two way communication, then it should be in both RX and TX. (But for simple packets, it doesn’t matter if it’s not there because the nRF sets up the Auto Retransmit Count as 3 by default with 250uS delay)

    • Alex says:

      Hi Mantico,

      I don’t have an Arduino sketch that has bidirectional communication but if you take a look the AlarmSM project that has bidirectional communication but really as you say, you just have to add it both TX and RX and it’s there by default anyway.

  46. harish says:

    hi alex,
    im new to both attiny84 and nrf24l01 module. please tell me where can i get the schematics for this and how to program attiny84.

  47. Tom says:

    Hello Alex,

    Great post! It works perfect on Arduino, tho I am newbie and I have no idea how to make it work with Atmel Studio 6.1.

    I get few errors all in mirf.c:

    Error 1 ‘byte’ undeclared (first use in this function) l: 63 c:34
    Error 3 expected expression before ‘)’ token l: 63 c:34
    Error 4 too few arguments to function ‘mirf_write_register’ l: 63 c: 34
    Error 6 expected expression before ‘)’ token l: 64 c: 31
    Error 7 too few arguments to function ‘mirf_write_register’ l: 64 c: 31

    Could you please help me out?

    Best regards and keep up great work!

    • Alex says:

      Hi Tom,

      You should be able to change all instances of byte to be “uint8_t”.

      • Tom says:

        Thank you very much for your incredibly fast reply!

        After changing to uint8_t I have bunch of other errors that just appeared in setup.c.

        Error 14 ‘DDRB’ undeclared (first use in this function) L: 65 C:2

        Same for PB0, PB1, PB2, PORTB

        And one more error for all instances of uint_8t:

        Error 5 unknown type name ‘uint8_t’ l: 18 c:2

        • Tom says:

          Ok, I got it further – needed to add in setup.c:


          Now I get errors regarding “multiple definitions” in all files for all functions including main

          • Tom says:

            Ok, finally got it working!

            Multiple definitions error was raised because you put .c files in #import

            I had to delete this, import nRF…h file into main.c otherwise STATUS and MAX_RT wouldn’t be recognised, as well as include avr/io.h and stdint.h to setup.c

            All compile well, let’s see how it works on microcontroller!

            Thanks again and best regards

  48. hari krishna prabu says:

    I am doing my M.Sc project in nrf24l01 using microcontroller i dont how to use it. pls tell me just how to transmit a data and receive that data. For that i need coding also.
    pls help me

    • Alex says:

      Hi Hari,

      In the post above there is example code for the Arduino and ATtiny85 with explanations, which parts do you need some help with?

  49. Ramon Panganiban says:

    Hi Alex,
    I am very new on mcus and I am building a project with using multiple Attiny85 and NRF24L01. I loaded you RX-TX tests V2.0 and it worked like a charm. However, out of the 15 Attiny I used, only 1 actually worked as tx although all works when loaded with the blink sketch. I tried reloading the Tx program but no luck. Only one is working.
    I am using WinAVR to burn the tx pgm to the Tiny using and Aduino Uno as programmer.
    I also tried your Arduino version using the Arduino IDE but same result. Can the other units be deffective?
    If you have any thoughts on how to solve this, it would be highly appreciated.
    Thank you very much.

    • Alex says:

      Hi Ramon,

      That seems a little strange that only 1 works. Can you try adding a 100uF capacitor very close to the nRF24?
      Also try adding more delay to the startup so instead of _delay_ms(50), try _delay_ms(500).
      You can also connect a LED with resistor to SCK and check if you see if dim/blink quickly when it sends the packet to the nRF.

      • Ramon Panganiban says:

        Hi Alex,
        Thanks for the very prompt response. Indeed, I have a 100uf cap at the point where the NRF24 power lines are connected on the breadboard. I will put an LED at the SCK pin today and will let you know. And by the way, I am running my test board off 3.3V from an ATX power supply.
        Again thanks, Alex.

      • Ramon Panganiban says:

        Hi Alex,
        Thank you for the advice. The LED blinked on load on all of them but again only 1 worked. It turned out that the chips were set at 8Mhz and I burned the bootloader at 1Mhz from the IDE Arduino 1.0.5 and still no go. I burned the fuses at 1 Mhz using avrdude directly and that solved the problem. All are now loaded and working.
        Thank you very very much. I really appreciated you advice.
        More power to you.

  50. Bob Lynas says:

    Hi Alex,
    I have been trying to configure a Nrf24l01 and a ATTiny85 together using the information in this blog.. http://nerdralph.blogspot.co.uk/2014/01/nrf24l01-control-with-3-attiny85-pins.html

    It uses a small amount of electronics to control the CSN line, freeing up another pin on the ATTiny85.

    Most of the page seems to discuss using this in TX mode.. What I really want is to only use this in RECEIVE only mode, I went through the process of Tieing CE high, then Low.. and now after reading your info I realise I will need one more pin so I can control CE from the micro, this will make 4 pins used for the nRF, leaving me one spare to control another device

    Any comments would be most useful

    Regards, Bob

    • Alex says:

      Hi Bob, just a quick thought on that, if all you need is for the CE to change high and low without the use of 1 more ATtiny pin, you could enable interrupts for MASK_RX_DR on the nRF. As you only use it for reading, it will go low when a packet is received (active low) and stay high when it’s waiting for packets so just tie this to CE pin and just clear the interrupt once the packet is read out.

      Edit: Actually since CE is kept high when no packet is received, you should be able to transmit too. You just have to check for any RX packets before you do as CE will stay low until they are read out or you could just clear the interrupt anyway before you send.

    • Den says:

      @ Bob

      I am after exactly the same thing !
      Please could you provide more detail or allow me to email you to chat?


  51. Den says:

    Hi Alex

    This looks like awesome work from what I can see. What an awesome tutorial.
    Have you checked out the RADIOHEAD libraries ? It would be awesome if you could add support there for the ATTINY85.

    I wonder if you could please help me.

    In my project I use a single TX on an Arduino to send to multiple receivers.
    Each receiver is an ATTINY85. And I only wish to send a character array of 22 bytes in the unint8 format.

    I downloaded your example code from the link in the article and saw that the TX has an Arduino file but the RX is all in C .. Is this correct ?
    If so , is it possible to you publish or make available an RX example in arduino format please or at least explain hot to do it ?

    Is it possible you could explain how to get the ATTINY85 connected to the NRF with a diagram for reference ?

    Last wish is ….
    Could you please explain how to get your code working with a 3-wire ATTINY85 and NRF24L01 using 3 pins as per this article :

    Look forward to hearing from you.


    • Alex says:

      Hi Den,

      1. Actually it’s the other way around, RX is in Arduino and TX for the ATtiny.
      2. Here’s the pin out diagram:
      nRF24L01 CE to PB3
      nRF24L01 SCK to PB2
      nRF24L01 CSN to PB4
      nRF24L01 MO to PB1
      nRF24L01 MI to PB0

      3. You should be able to use the circuit they have but you’ll just need to add delays like they did.
      Find: mirf_CSN_lo;
      After add: delayMicroseconds(8);

      Find: mirf_CSN_hi;
      After add: delayMicroseconds(64);

      • Den says:

        Hi Alex

        Thanks a million for your prompt reply 🙂
        I need the ATTINY85 as the RX and the Arduino as the TX.
        Each ATTINY85 is intended to be a receiver.
        I will need at least 1 PIN as output on the ATTINY85.
        Can you please help ?

        Kind regards


        • Alex says:

          Hi Den,

          Here’s an example I just re-made: http://www.insidegadgets.com/wp-content/uploads/2014/08/ATtiny_RX-Arduino_TX.zip (you may need to change the Attiny chip in the makefile)

          For the 1 Pin as output on the ATtiny, you can just tie CE to high.
          Edit: You also have to run mirf_config_register(STATUS, 1<

          • den says:

            AWESOME 🙂
            When you say you re-made it .. do you mean you have it working ?
            What do you mean by change ATTINY chip ?
            Mirf config register status ??

            • Alex says:

              Yes I’ve tested the code I linked to and it’s working.
              In the makefile I used an ATtiny25, so you just have to edit the makefile to change to ATtiny85.

              If you tie CE to high to save 1 pin on the ATtiny you need to add:
              mirf_config_register(STATUS, 1<

              • den says:

                hey Alex

                Thank you
                What IDE whoucl I open that all in as there is no INO file for the RX.

                I know this is probably a tall order but is there any chance of
                1.a schematic of it all
                2.the same example using maniacbug RF24 library please.

                Ever hopeful


  52. Andrea says:

    Hi Alex, i’m working with Attiny84 (w/ Mirf) and nrf24l01 to comunicate with another one on Arduino(w/Mirf). i had make a cute pcb for the attiny and the sketch is simply, but now i need to make comunication from attiny and RPI. i have think to try mirf on attiny and rf24 on Arduino, after a success i can try to use RPI. But now i have a problem to understand the address to use to make attiny “visible” .
    Now i use this sketch on attiny:

    #define MIRF_PAYLOAD 32
    byte txData[MIRF_PAYLOAD + 1];
    const char msg[]=”Messaggio inviato da Attiny84″;
    int msgIndex=0;

    const byte button=2;

    void wake()
    sleep_disable (); // first thing after waking from sleep:
    detachInterrupt (0); // stop LOW interrupt

    void goToSleep ()
    noInterrupts (); // make sure we don’t get interrupted before we sleep
    ADCSRA = 0; // turn off ADC
    power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
    attachInterrupt (0, wake, LOW); // wake up on low level
    interrupts (); // interrupts allowed now, next instruction WILL be executed

    } // end of goToSleep

    void setup() {

    digitalWrite (button, HIGH); // internal pull-up

    PCMSK0 |= (1<<PB2);
    MCUCR = (1<<ISC01) | (1<<ISC00);
    GIMSK |= (1<<INT0);
    Mirf.csnPin=8 ;

    void loop() {

    My question is : how can i write a correct address in rf24 receving sketch? i don't understand the hex address…
    thanks for all

    • Alex says:

      Hi Andrea,

      Could you paste your code on pastebin or similar as I may not be able to see your receive address.
      You can just use something like (byte*) “serv1” like is done for the TX address.

  53. rani says:

    can anyone help me to make programming for nrf24l01 with atmega16 on both sides…
    anyone who succeed in doing tx and rx correctly please help me…

  54. rani says:

    hi alex i burn tx program on one side and i made rx program for receiving but it not working

  55. Robert says:

    Hi Alex
    This is really useful. I want to be able to read accelerometer values and then transmit them, but I’m not sure if there are enough pin on the ATTINY85 to do this. The accelerometer also uses SPI, and takes up four pins: MOSI, MISO, CS and Clock. Pin 1 on the ATTINY85 is configured as an interrupt (so that the accelerometer can wake up the ATTINY85 when necessary). This leaves one spare pin.

    As I understand it, the NRF24 can share the MOSI, MISO and clock pins with the accelerometer. Hopefully there is also a way of setting it to transmit/receive without tying up another pin permanently. So I am thinking that I can use the one spare pin on the ATTINY to be the chip select pin, selecting when the NRF24 is active. Do you agree? But if so, how/where do I define which pin on the ATTINY is the select pin for this device, rather than selecting the accelerometer?

    Many thanks

    • Alex says:

      Hi Robert,

      Yes the nRF can share the pins with the accelerometer. When you send SPI transfer commands, you manually specify which CS pin to put low yourself.

      You could actually hook up all nRF pins to the ATtiny (except for IRQ) and then re-use the CS pin for both the nRF and accelerometer with an inverter.

      So if CS is low, the CS to the nRF is low so you select that and the inverter deselects the accelerometer which shows it’s CS as high; and vice versa.

  56. Robert says:

    Thanks – good thought. I can get a pin back!

  57. Adil says:

    Hi. I am trying to transfer a PPM(Pulse-position modulation) signal between two NRF24L01+ using the RF24 library. They are connected to their respective Arduino Nano. PPM signal comes in on pin 2 on TX which is supposed to be transferred to RX by wireless transmission. RX then puts out the signal on pin 2.
    I am getting a very out of sync signal. Sometimes I see an increase or decrease on a channel but no real time control whatsoever. This setup is for a diy transmitter and receiver for a quadcopter.

    Code for RX and TX is given here. Any suggestios are really appreciated.
    TX: http://pastebin.com/AjuKTNJe
    RX: http://pastebin.com/Bjcxv43M

    A graphic presentation of PPM signal:

    • Alex says:

      Hi Adil,

      With the payload size, I don’t know if that’s binary 10 for 2 bytes or 10 for 10 bytes – so just double check you are only sending as many bytes as you need to.

      Your retries is set to the maximum of 15 and also I’m guessing the other 15 is for the Auto Retransmit Delay which means it will try to re-send a packet 15 times and wait 4ms in between each send attempt if it can’t get to the RX. For streaming application, I would set it to 0 because if 1 packet is lost, you would send the next packet quickly enough that you don’t really need to bother retrying.

      Apart from that, just make sure that your TX/RX are sending as fast as possible and it may be worth timing how long it takes for you to read out the packets in the RX side (sometimes it can take a few ms).

      250Kbps will give you more range but 1-2Mbits will give you less chance of collisions, though for a remote control application like yours I would also want range.

      Also as you are using Arduino code, that can slow things down a bit even to set a pin high or low, so try using the raw commands like PORTB |= (1<

  58. Suhas says:

    Is there any place to get all the modified code in place (may be on github)?

    Thanks in advance for the tutorial…

  59. Rick says:

    Hi, I just found your code and I am really interested in working it. Just a simple question to start with. Where do I get the avr/io.h and util/delay.h libraries? Are those only available on Atmel Studio?

  60. Ramon Panganiban says:

    Hi Alex,

    Moving on from the Attiny/Nrf24l01, seems to me that the smaller Nrf24le1 is an improvement on projects requiring small footprint tranceivers. I am doing another project and have tried porting the Arduino/Attiny85 pgm to the nRf24le1 but no luck. All I need is to activate the spi transfer procedure and I have been in a rut for a week now. Of course I am total newbie on mcus especially 8051. I would appreciate it very much if you can point me in the right direction.

    Best regards and more power!!

    • Alex says:

      Hi Ramon,

      The nrf24le1 sounds very interesting, an all in one MCU/RF with encryption and RNG. I also haven’t used the 8051 before but have you had a look at the application note – nAN-15 Creating Applications with the Keil C51 C Compiler. Looks like their nRFgo SDK should come with RF examples you could tweak.

  61. najeeb says:

    hai.i am new to nrf.can you tell me how to use how to use atmega8 and arduino with nrf.

  62. karl says:

    hello, let me drop an inquire, may be i am wrong but i cant find info about it, what i need is to connect a nrf24l01 to a 74hc595 (via SPI) to light 8 leds (through the shift register with latch), my requested help is: if it is posible to do it without an arduino or another microcontroler, independent of the transmitter part. i will apreciate very much your opinion, thank you in advance. karl

    • Alex says:

      Hi Karl,

      The nRF24L01 doesn’t allow you to program the processor, so you won’t be able to shift out to the LED without an additional microcontroller. I have heard of other nRF modules that that do allow you to program your code on the processor.

  63. Avinash says:

    I have problem
    My project is temp sensing via lm 35 and transmit (wireless )data and receive at other side using NRF24L01 using Arduino
    I need help
    I can not doing transmission and receving.

Leave a Reply to Alex