Tutorial: Ultra Low Cost 2.4 GHz Wireless Transceiver with the FRDM Board

For my embedded systems lecture I need a wireless connection to the robot we will develop during that course. So far I have SMAC (IEEE802.15.4) and Bluetooth worked out. But that IEEE802.15.4 (ZigBee) is expensive, and the cheap Bluetooth modules are great for robot-to-host connection, but not for swarm robots which need to communicate to each other. Alex Vecchio (see this post) pointed me to a $2.75 (!) wireless module featuring the Nordic Semiconductor nRF24L01+. Exactly what I needed, with an incredible low price :-).

nRF24L01+ Module Detail

nRF24L01+ Module Detail

So I ordered a handful modules, and after a few hours, I had two FRDM-KL25Z talking to each other:

nRF24L01+ Setup with FRDM-KL25Z

nRF24L01+ Setup with FRDM-KL25Z

So here is what I have documented while developing my first application with two FRDM-KL25Z and two nRF24L01+ modules…….

Nordic Semiconductor nRF24L01+

Nordic Semiconductor has this nRF24L01+ ultra low power 2.4 GHz ISM band wireless solution device.

❗ Note that the ‘+’ version is the newer one and recommended to be used. Be aware that some module vendors still might sell the non-+ version.

Key features of the nRF24L01+ are (source: nRF24L01+ data sheet):

  • Worldwide 2. GHz ISM band (free, unlicensed band)
  • 250 kbps, 1 Mbps and 2 Mbps on air data rates
  • Ultra low power (11.3 mA Tx with 1 mW output power, down to 26 μA in standby-I and 900 nA in power down mode)
  • 1.9 – 3.6V supply voltage, with 5V tolerant input pins
  • Automatic acknowledge sending with automatic retries
  • RX and TX FIFO’s with ACK user data possibility
  • Up to 6 data pipes/addresses for simplified star network
  • Simple 8 pin (7 pin without IRQ) SPI interface: VCC, GND, CE, CSN, SCK, MISO, MOSI and optional IRQ.

❗ The supply voltage is really up to 3.6V. Using a supply voltage of 5V will destroy the module!

I ordered mine from yourduino.com which sells the module for only $2.75, see here for the layout and the schemata):

nRF24L01+ Module

nRF24L01+ Module

❗ If you search the web, many other vendors are selling this module too, for less than $5.

Module Layout:

NRF24L01 Layout and Pins

NRF24L01 Layout and Pins (Source: GitHub)

Module Schematics:

nRF24L01 Schematic

nRF24L01 Schematic (Source: GitHub)

Block Diagram of nRF24L01+:

nRF24L01 Block Diagram

nRF24L01 Block Diagram (Source: Nordic Semiconductor Data Sheet nRF24L01P_Product_Specification_1_0.pdf)

Pin and SPI Connection

The module has following pins connecting to a microcontroller:

  1. GND: Ground.
  2. VCC: 3.3V.
  3. CE: Chip (RX/TX) Enable, high active. If high, module is either sending or listening.
  4. CSN: Chip Select Not, low active. If low, the chip responds to SPI commands. This is actually the ‘real’ chip select signal, and is easily confusing with CE which enables/disables the transceiver radio part.
  5. SCK: SPI Shift Clock, up to 10 MHz.
  6. MOSI: Master-Out-Slave-In, used to shift data from the microcontroller to the device.
  7. MISO: Master-In-Slave-Out, used to shift data from the device to the microcontroller.
  8. IRQ: Optional Interrupt Request pin. Signals RX/TX status like packet sent or received.

Bit/Byte order: The SPI needs to be configured to send the Most Significant Bit First. within a byte. For multiple data bytes, the Least Significant Byte needs to be shifted first.

Sender and Receiver Application

Time to get some hands-on! For this I’m using two RF modules with two FRDM-KL25Z. One board is sending data to the other, and they indicate with LED blinking proper operation. I’m using interrupts, but to keep things simple, I will only set a flag in the ISR I will do the radio processing outside of the interrupt service routine.

💡 Just setting a flag in the ISR keeps the interrupt service routine short and sweet, and I do not block further interrupts by accessing the radio module over the SPI bus. Additionally I use the SPI bus with interrupts, so doing interrupt-based SPI within another interrupt needs careful interrupt level planning. To keep things simple, I will not do this here.

Hardware Wiring

First, I need to wire the RF module to the processor. I’m using Processor Expert with CodeWarrior as this simplifies a lot. In this tutorial I’m using the following pin mapping:

LED_BLUE [Output]        => ADC0_SE5b/PTD1/SPI0_SCK/TPM0_CH1 [74]
LED_GREEN [Output]       => TSI0_CH12/PTB19/TPM2_CH1 [54]
LED_RED [Output]         => TSI0_CH11/PTB18/TPM2_CH0 [53]
RF_CE [Output]           => PTB9 [48]
RF_CSN [Output]          => PTB8/EXTRG_IN [47]
RF_IRQ [Input]           => PTD0/SPI0_PCS0/TPM0_CH0 [73]
RF_MISO [Input]          => PTE3/SPI1_MISO/SPI1_MOSI [4]
RF_MOSI [Output]         => ADC0_SE7b/PTD6/LLWU_P15/SPI1_MOSI/UART0_RX/SPI1_MISO [79]
RF_SCK [Output]          => PTE2/SPI1_SCK [3]
  1. Make sure you have any extra components loaded (e.g. Wait and LED, see here).
  2. Create a Processor Expert project with the wizard (File > New Bareboard Project), then select your CPU and enable Processor Expert.
  3. Add the Wait component to your project. It is optional, and I’m using it in the demo application to wait for a given number of milli-seconds. Alternatively you can burn cycles in a loop.
  4. Add LED components to your project as needed (see this post). I’m using them to indicate the TX and RX status. Alternatively you can use BitIO component instead, or left it out.
  5. Now I’m going to add the components for the hardware connection to the module. If you are going to use different pins, then assign different pin names (of course).
  6. Add a BitIO component for CE: It is configured as output pin with initial LOW value. LOW means ‘not sending/listening’, so this is a good initialization value.

    CE Properties

    CE Properties

  7. Add a BitIO component for CSN: It is configured as output pin with initial HIGH value. CSN is pulled LOW to send commands to the transceiver.

    CSN Properties

    CSN Properties

  8. Add an ExtInt component for the interrupt pin: The interrupt is active low, so we need to react on falling edge:

    IRQ Properties

    IRQ Properties

  9. Next to add SynchroMaster component for SPI:I need to assign the MISO, MOSI and CLK pins. Clock edge is on falling edge with MSB first, with LOW idle clock state. I’m using a fairly slow clock speed for now (it could go up to 10 MHz, but better to start slowly :-))

    SPI Properties

    SPI Properties

💡 In above settings I have configured an Input and Output buffer size. This would allow me to send data in blocks. To keep things simple, I will send in this tutorial the data to the SPI character by character. Not ideal from a performance point of view, but again: we keep things simple. Once things are working, it is time to optimize things.

For me it looks now like this:

Project with components

Project with components

Source Files

With File > New > Source File and File  New > Header File  I’m adding

  • Application.c: this is the application main file
  • Application.h as interface for Application.c
  • nRF24L01.c: Driver for the Transceiver
  • nRF24L01.h interface to nRF24L01.c

The content of the files are posted at the end of this article. I’m explaining now what needs to be done to send and receive data. What is common is the initialization code.


First, the application initilizes the driver and calls RF_Init():

WAIT1_Waitms(100); /* give device time to power up */
RF_Init(); /* set CE and CSN to initialization value */

Actually, it only sets the CE and CSN pins:

 * \brief Initializes the transceiver.
void RF_Init(void) {
  RF_CE_LOW();   /* CE high: do not send or receive data */
  RF_CSN_HIGH(); /* CSN low: not sending commands to the device */

Next, it writes to configures the device using RF_WriteRegister(), configuring the output power (RF24_RF_SETUP_RF_PWR_0) and configures the data rate (250 kbit, RF24_RF_SETUP_RF_DR_250):

  RF_WriteRegister(RF24_RF_SETUP, RF24_RF_SETUP_RF_PWR_0|RF24_RF_SETUP_RF_DR_250);

It is using the RF_SETUP (address 0x06) register:

Address (Hex) Mnemonic Bit Reset Value Type Description
  RF Setup Register
R/W Enables continuous carrier transmit when high.
R/W Only ‘0’ allowed
R/W Set RF Data Rate to 250kbps. See RF_DR_HIGHfor encoding.
R/W Force PLL lock signal. Only used in test
R/W Select between the high speed data rates. This bitis don’t care if RF_DR_LOW is set.Encoding:[RF_DR_LOW, RF_DR_HIGH]:‘00’ – 1Mbps‘01’ – 2Mbps‘10’ – 250kbps‘11’ – Reserved
R/W Set RF output power in TX mode’00’ – -18dBm’01’ – -12dBm’10’ – -6dBm’11’ – 0dBm
  Don’t care

I’m using a method RF_WriteRegister() which writes a register on the transceiver:

void RF_WriteRegister(uint8_t reg, uint8_t val) {
 * \brief Write a register value to the transceiver
 * \param reg Register to write
 * \param val Value of the register to write
void RF_WriteRegister(uint8_t reg, uint8_t val) {
  RF_CSN_LOW(); /* initiate command sequence */
  (void)SPI_WriteRead(RF24_W_REGISTER|reg); /* write register command */
  (void)SPI_WriteRead(val); /* write value */
  RF_CSN_HIGH(); /* end command sequence */
  RF_WAIT_US(10); /* insert a delay until next command */

The program uses several macros to hide low-level functionality for portability:

/* Macros to hide low level functionality */
#define RF_WAIT_US(x)  WAIT1_Waitus(x)  /* wait for the given number of micro-seconds */
#define RF_WAIT_MS(x)  WAIT1_Waitms(x)  /* wait for the given number of milli-seconds */
#define RF_CE_LOW()    CE_ClrVal()      /* put CE LOW */
#define RF_CE_HIGH()   CE_SetVal()      /* put CE HIGH */
#define RF_CSN_LOW()   CSN_ClrVal()     /* put CSN LOW */
#define RF_CSN_HIGH()  CSN_SetVal()     /* put CSN HIGH */

I’m using the following method to write (and read from) the SPI:

* \brief Writes a byte and reads the value
* \param val Value to write. This value will be shifted out
* \return The value shifted in
static uint8_t SPI_WriteRead(uint8_t val) {
  uint8_t ch;

  while (SM1_GetCharsInTxBuf()!=0) {} /* wait until tx is empty */
  while (SM1_SendChar(val)!=ERR_OK) {} /* send character */
  while (SM1_GetCharsInTxBuf()!=0) {} /* wait until data has been sent */
  while (SM1_GetCharsInRxBuf()==0) {} /* wait until we receive data */
  while (SM1_RecvChar(&ch)!=ERR_OK) {} /* get data */
  return ch;

The code uses RF24_W_REGISTER (0x20) which is used to mark a ‘write’ command. The RF_SETUP register is 0x06, so together this build the value 0x26 on the bus. RF24_RF_SETUP_RF_PWR_0|RF24_RF_SETUP_RF_DR_250 together build as well the value 0x26. So what is sent over the bus is this:



As you can see, the transceiver always responds with the STATUS byte. This status can be polled with a NOP command too. This is implemented with

* \brief Read and return the STATUS
* \return Status
uint8_t RF_GetStatus(void) {
  return RF_WriteRead(RF24_NOP);

On the bus this looks like this:

NOP (0xff) command to get STATUS register

NOP (0xff) command to get STATUS register


Next, I configure the payload (amount of data transmitted). It is possible to use variable payload, but in my example I’m using a fixed size wich is defined in the PAYLOAD_SIZE macro. Payload size is configured with register 0x11 (RX_PW_P0) for communication channel 0 (I going to use only one communication channel):

  RF_WriteRegister(RF24_RX_PW_P0, PAYLOAD_SIZE); /* number of payload bytes we want to send and receive */


And then I configure it to use the RF channel (macro CHANNEL_NO):

  RF_WriteRegister(RF24_RF_CH, CHANNEL_NO); /* set channel */

RX and TX Address with address matching

In need to assign an address for the TX and RX channels, and enable address matching:

static const uint8_t TADDR[5] = {0x11, 0x22, 0x33, 0x44, 0x55}; /* device address */

/* Set RADDR and TADDR as the transmit address since we also enable auto acknowledgment */
RF_WriteRegisterData(RF24_RX_ADDR_P0, (uint8_t*)TADDR, sizeof(TADDR));
RF_WriteRegisterData(RF24_TX_ADDR, (uint8_t*)TADDR, sizeof(TADDR));

/* Enable RX_ADDR_P0 address matching */
RF_WriteRegister(RF24_EN_RXADDR, RF24_EN_RXADDR_ERX_P0); /* enable data pipe 0 */

Sender or Receiver

So far the initialization code is the same for sender and receiver. Now things are bit different, distinguished by the macro IS_SENDER which I use locally in my application:

  RF_WriteRegister(RF24_EN_AA, RF24_EN_AA_ENAA_P0); /* enable auto acknowledge. RX_ADDR_P0 needs to be equal to TX_ADDR! */
  RF_WriteRegister(RF24_SETUP_RETR, RF24_SETUP_RETR_ARD_750|RF24_SETUP_RETR_ARC_15); /* Important: need 750 us delay between every retry */
  TX_POWERUP();  /* Power up in transmitting mode */
  CE_ClrVal();   /* Will pulse this later to send data */
  RX_POWERUP();  /* Power up in receiving mode */
  CE_SetVal();   /* Listening for packets */

I’m going to use ‘auto acknowledge’: with this, the sender will transparently handle an acknowledge. For this I need to configure the retry time if communication fails for 750 μs and 15 retries.

Next, two macros are used:

/* macros to configure device either for RX or TX operation */
#define TX_POWERUP()   RF_WriteRegister(RF24_CONFIG, RF24_EN_CRC|RF24_CRCO|RF24_PWR_UP|RF24_PRIM_TX) /* enable 2 byte CRC, power up and set as PTX */
#define RX_POWERUP()   RF_WriteRegister(RF24_CONFIG, RF24_EN_CRC|RF24_CRCO|RF24_PWR_UP|RF24_PRIM_RX) /* enable 2 byte CRC, power up and set as PRX */

Both macros write the CONFIG register: it enables CRC (EN_CRC) with 2 byte CRC (CRCO), powers up the transceiver (PWR_UP). The only difference between sender and receiver is the PRIM_TX flag which tells the configuration register to be the sender.

Below are the details about the CONFIG register:

CONFIG Register (Source: nRF24L01P Data Sheet)

CONFIG Register (Source: nRF24L01P Data Sheet)

❗ Note that there are 3 ‘interrupt mask’ or ‘interrupt inhibit’ bits. I’m intentionally *not* setting these bits because I want to use interrupts (more later).

The last part is to set the CE pin either low or high: setting it HIGH will let the receiver start listening. The CE pin is set high on the sender to initiate sending the data.

Status Register

The transceiver is having 3 bits in the status register to tell

  1. if data transmission was successful
  2. if data has been received
  3. if sending was not possible (maximum retry reached)

Before going actually to send/receive, I’m going to reset the 3 bits with the following routine:


which is implemented as

* \brief Reset the given mask of status bits
* \param flags Flags, one or more of RF24_STATUS_RX_DR, RF24_STATUS_TX_DS, RF24_STATUS_MAX_RT
void RF_ResetStatusIRQ(uint8_t flags) {
  RF_WriteRegister(RF24_STATUS, flags); /* reset all IRQ in status register */

With this, we are ready to send and receive data :-).

Sending the Data

Sending data is performed by

RF_TxPayload(payload, sizeof(payload)); /* send data */

which is implemented as:

* \brief Send the payload to the Tx FIFO and send it
* \param payload Buffer with payload to send
* \param payloadSize Size of payload buffer
void RF_TxPayload(uint8_t *payload, uint8_t payloadSize) {
  RF_Write(RF24_FLUSH_TX); /* flush old data */
  RF_WriteRegisterData(RF24_W_TX_PAYLOAD, payload, payloadSize); /* write payload */
  RF_CE_HIGH(); /* start transmission */
  RF_WAIT_US(15); /* keep signal high for 15 micro-seconds */
  RF_CE_LOW();  /* back to normal */

What it does is first flushing the TX FIFO with writing to the FLUSH_TX register, just in case there is still something in there). Then it writes the payload data with W_TX_PAYLOAD.

❗ The number of bytes transmitted as payload needs to be the number of payload bytes specified above with the write the RX_PW_P0 register!

Finally it sends a pulse with the CE pin of at least 15 μs to initiate the sending of the data over the air.

W_TX_PAYLOAD command with Flush

W_TX_PAYLOAD command with Flush


As pointed out above, I’m not setting the interrupt mask bits in the CONFIG register. In my application, I route the interrupts to the following handler, both for the sender and receiver:

static volatile bool isrFlag; /* flag set by ISR */

void APP_OnInterrupt(void) {
  CE_ClrVal(); /* stop sending/listening */
  isrFlag = TRUE;

I’m only setting a flag in the interrupt routine and pulling CE low to stop listening (if I’m listening). That function is called from Events.c:

** ===================================================================
**     Event       :  IRQ_OnInterrupt (module Events)
**     Component   :  IRQ [ExtInt]
**     Description :
**         This event is called when an active signal edge/level has
**         occurred.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
void IRQ_OnInterrupt(void)

Inside my sender main loop, I’m checking that interrupt flag and reset the STATUS flags as needed:

if (isrFlag) { /* check if we have received an interrupt */
  isrFlag = FALSE; /* reset interrupt flag */
  status = RF_GetStatus();
  if (status&RF24_STATUS_RX_DR) { /* data received interrupt */
    RF_ResetStatusIRQ(RF24_STATUS_RX_DR); /* clear bit */
  if (status&RF24_STATUS_TX_DS) { /* data sent interrupt */
    cntr = 0; /* reset timeout counter */
    LEDR_Off(); /* indicate data has been sent */
    RF_ResetStatusIRQ(RF24_STATUS_TX_DS); /* clear bit */
  if (status&RF24_STATUS_MAX_RT) { /* retry timeout interrupt */
    RF_ResetStatusIRQ(RF24_STATUS_MAX_RT); /* clear bit */

This then looks like this:

Interrupt after data has been sent

Interrupt after data has been sent

Resetting the interrupt bit is needed to get the IRQ line back to HIGH. The zoom below shows that sequence in more details: It queries the status (0xFF instruction) which returns 0x1E (max retry reached, all FIFOs empty). Then it uses 0x27 command to reset the 0x10 bit.

Resetting Interrupt Flag

Resetting Interrupt Flag

If data has been sent and acknowledge received, the 0x20 bit is set:

Interrupt with successful data sent

Interrupt with successful data sent


Things are very similar on the receiver side: I check if an interrupt occured, then checking the flags. If the STATUS_RX_DR bit is set, it reads the data with RxPayload():

    if (isrFlag) { /* interrupt? */
      isrFlag = FALSE; /* reset interrupt flag */
      cntr = 0; /* reset counter */
      LEDG_Neg(); /* blink green LED to indicate good communication */
      status = RF_GetStatus();
      if (status&RF24_STATUS_RX_DR) { /* data received interrupt */
        RF_RxPayload(payload, sizeof(payload)); /* will reset RX_DR bit */
        RF_ResetStatusIRQ(RF24_STATUS_RX_DR|RF24_STATUS_TX_DS|RF24_STATUS_MAX_RT); /* make sure we reset all flags. Need to have the pipe number too */
      if (status&RF24_STATUS_TX_DS) { /* data sent interrupt */
        RF_ResetStatusIRQ(RF24_STATUS_TX_DS); /* clear bit */
      if (status&RF24_STATUS_MAX_RT) { /* retry timeout interrupt */
        RF_ResetStatusIRQ(RF24_STATUS_MAX_RT); /* clear bit */
    } else {
      if (cntr>500) { /* blink every 500 ms if not receiving data */
        cntr = 0; /* reset counter */
        LEDB_Neg(); /* blink blue to indicate no communication */
      WAIT1_Waitms(1); /* burning some cycles here */

The receiver routine is similar to the sender one:

 * \brief Receive the Rx payload from the FIFO and stores it in a buffer.
 * \param payload Pointer to the payload buffer
 * \param payloadSize Size of the payload buffer
void RF_RxPayload(uint8_t *payload, uint8_t payloadSize) {
  RF_CE_LOW(); /* need to disable rx mode during reading RX data */
  RF_ReadRegisterData(RF24_R_RX_PAYLOAD, payload, payloadSize); /* rx payload */
  RF_CE_HIGH(); /* re-enable rx mode */

It is using a method which reads the multiple payload bytes:

* \brief Read multiple bytes from the bus.
* \param reg Register address
* \param buf Buffer where to write the data
* \param bufSize Buffer size in bytes
void RF_ReadRegisterData(uint8_t reg, uint8_t *buf, uint8_t bufSize) {
  SPI_WriteReadBuffer(buf, buf, bufSize);

That’s it :-).

With this I’m able to send and receive data.

Source Code

The source code of this application is available on GitHub here.


This tutorial does not cover all the powerful aspects of the nRF24L01+, but should get you up and running quickly. I plan to add more functionality in the future and to create a dedicated Processor Expert component for this RF chip. Until then, I hope this tutorial is useful for you.

Happy Communicating 🙂

136 thoughts on “Tutorial: Ultra Low Cost 2.4 GHz Wireless Transceiver with the FRDM Board

  1. sir can i know whether any other rf transceivers are available whose transmit and receive power is very very low less than micro watt and it should have specification as 802.15.4 in 2.4Ghz. its urgent for my project.


    • Hi Maria,
      Your requirements is rather vague, and the transmit power depends on many factors (effective transmitted, antenna output, etc). Have a look at the 2.4 GHz offerings from TI, Nordic Semiconductor and NXP.


    • hi maria, if it isn’t too late, take a look at the nRF24LE1 from Nordic. it has the radio AND a fast-ish C51 8-bit microprocessor that runs off a coin cell, $3.50 fully functional board on ebay. tons of A/D. there’s a GCC compiler and the “native” keil compiler (free for smaller programs, kinda expensive when you go bigger). there are a few versions available on ebay. start with the one with the pins, then go to the 2/3rds sized board, same functionality. they’re about the size of your thumbnail :-).


  2. Hi Erick!
    I have a bonch of doubts hahaha

    In nRF24L01.c…

    Doubt No.1
    #define RF_WAIT_US(x) WAIT1_Waitus(x)
    Can I write that part without “x” in brackets?

    Doubt No.2
    Why are some funtions “static”? could they be just “uint8_t” or “void” etc.?

    Doubt No.3
    while (SM1_SendChar(val)!=ERR_OK) {} /* send character */
    Is “ERR_OK” reserved word o value?

    Doubt No.4
    In Application.c…
    Why do you use #if #else #endif in some functions? Could you explain them to me, please?

    I hope you can help me 😀 it would be very useful 🙂
    Thank you 🙂


    • Hi Abraham,
      these are undoubtful very basic C programming questions, so I hope you have learned C programming at least a bit :-).
      In any case, here are some answers which should help you going:
      1) No, as x is needed parameter to specify the waiting time in micro seconds
      2) Static functions mean they have static linkage in C. Static should be used for functions which are not used from other modules.
      3) ERR_OK is a #define for a predefined error code, defined in PE_Error.h
      4) These are C programming language preprocessor directives to turn on/off some parts of the code instead testing this at runtime.

      I hope this helps,


  3. Hi Thanks. It helped me 🙂
    I have two question more…
    how do “Input buffer size” and “output buffer size” work in an interrupt?
    A SPI interrupt occurs when FRDM have tramitted o received the amount “Input buffer size” or “Output buffer size” that you specified?

    Thanks again 🙂


  4. Hi! I just recently stumbled upon your blog and it is really interesting.
    I’m trying to implement this RF module in a school project and I was wondering:

    Is there a way to implement 2 transmitters to only 1 receiver? Just looking for advice so I can get on track.

    Greetings from Mexico! 🙂


    • Hi Miguel,
      Yes, check the Nordic Semiconductor nRF24L01+ data sheet, chapter 7.6 Multiceiver: using pipes, you can connect up to 6 devices within an address range. The device does not have a real broadcast, but with re-transmitting you can even reach many more devices even if they share the same address.


  5. Erich, please see this image:
    You can me help? I do not understand, trying read a setup_aw register of the nrf24l01+, for example, but I not has success, because no data received. Where is my error?


    • Your CSN high time seems to be very short. Are you within the timing as specified in the data sheet?
      I tried your sequence with my code, and the transceiver properly returns the register value for me.


  6. Hello Erich,
    I am currently working with the module and I noticed that the function RF_RxPayload does not do what is seems to.
    void RF_RxPayload(uint8_t *payload, uint8_t payloadSize) {
    RF_CE_LOW(); /* need to disable rx mode during reading RX data */
    RF_ReadRegisterData(RF24_R_RX_PAYLOAD, payload, payloadSize); /* rx payload */
    RF_CE_HIGH(); /* re-enable rx mode */


    I understand that this function reads the payload( 1 – 32 bytes). The read operation always starts at byte 0 and then the payload is deleted from FIFO after it is read. But when I try to send a shorter payload after a long one, the rest of the first payload is kept.
    I wonder if this is a problem of missing configuration? I will appreciate if you could give me a hand on this.

    Thank you in advance,


  7. Hello, I have a question… Can I use only one microcontroller KL25Z for Transceiver wireless module… Is it possible, that KL25Z will be able to switch, from sender to receiver ? Thanks for answer


  8. Hello Erich, I’m trying to implement your tutorial, if you can help me with a question, how do I communicate the two boards?


    • In the source (application.c) there is a define

      #define IS_SENDER 0 /* 1 if we are the sender, 0 if we are the receiver */

      Have that one set to 1 for the sender and to 0 for the receiver.

      I hope this helps,


  9. hello… this little module seems to be what i am looking to build some toys for my pup… a quick question about this transceiver (and sorry i haven’t done more research on my side).
    what is the transmission range for this transceiver? does it need line of sight or can i have receivers on different rooms and still work? (something like a simple wifi network? )

    thanks in advance for your comments.


    • it is up to 2 MBit (includes protocol overhead). Range depends on antenna design, but that module has a line-of-sight range of 50 meters. Inhouse it is around 20 meters and can pass a normal wall.


What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.