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.

:idea: 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:

=================================================================
 SIGNAL LIST
-----------------------------------------------------------------
SIGNAL-NAME [DIR]        => PIN-NAME [PIN-NUMBER]
-----------------------------------------------------------------
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

:idea: 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.

Initialization

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
06
RF_SETUP
  RF Setup Register
CONT_WAVE
7
0
R/W Enables continuous carrier transmit when high.
Reserved
6
0
R/W Only ‘0’ allowed
RF_DR_LOW
5
0
R/W Set RF Data Rate to 250kbps. See RF_DR_HIGHfor encoding.
PLL_LOCK
4
0
R/W Force PLL lock signal. Only used in test
RF_DR_HIGH 3
1
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
RF_PWR
2:1
11
R/W Set RF output power in TX mode’00’ – -18dBm’01’ – -12dBm’10’ – -6dBm’11’ – 0dBm
Obsolete
0
  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:

RF_SETUP Write

RF_SETUP Write

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

Payload

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 */

Channel

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:

#if IS_SENDER
  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 */
#else
  RX_POWERUP();  /* Power up in receiving mode */
  CE_SetVal();   /* Listening for packets */
#endif

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 1 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 1 byte CRC, power up and set as PRX */

Both macros write the CONFIG register: it enables CRC (EN_CRC) with 1 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:

RF_ResetStatusIRQ(RF24_STATUS_RX_DR|RF24_STATUS_TX_DS|RF24_STATUS_MAX_RT);

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_WAIT_US(10);
  RF_CSN_LOW();
  RF_WAIT_US(10);
  RF_WriteRegister(RF24_STATUS, flags); /* reset all IRQ in status register */
  RF_WAIT_US(10);
  RF_CSN_HIGH();
  RF_WAIT_US(10);
}

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

Interrupts

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)
{
  APP_OnInterrupt();
}

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

Receiver

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 */
      LEDB_Off();
      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 {
      cntr++;
      if (cntr>500) { /* blink every 500 ms if not receiving data */
        cntr = 0; /* reset counter */
        LEDG_Off();
        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) {
  RF_CSN_LOW();
  (void)SPI_WriteRead(RF24_R_REGISTER|reg);
  SPI_WriteReadBuffer(buf, buf, bufSize);
  RF_CSN_HIGH();
  RF_WAIT_US(10);
}

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.

Summary

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 :-)

About these ads

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

      • Hi Alex,
        yes, I have seen this. But this is implemented in C++ which is less than ideal, and it is using a GPL version of licensing, which is very restrictive and not user friendly in my view. Of course you are open to use it, but especially because of the licensing terms I will not use it for my projects.

        Like

    • Yes, it was not that difficult with the help of a logic analyzer. Only the interrupt mode was tricky, as most examples and drivers I have found on the internet actually do not use it. Still I need to work on a dedicated Processor Expert component for the transceiver to make things even easier.

      Like

  1. Pingback: Kinetis ARM Cortex M4 DIY Board for $5 | MCU on Eclipse

  2. Pingback: Mini Sumo Robot Competition running with FRDM-KL25Z | MCU on Eclipse

  3. Pingback: Zumo Robot Chassis PCB arrived! | MCU on Eclipse

  4. Pingback: Zumo Robot assembled | MCU on Eclipse

  5. Pingback: Tutorial: Using the FRDM-KL25Z as Low Power Board | MCU on Eclipse

  6. thanks again! for that great work. I have a question how can the module be a sender and a receiver in the same time? do you have to create some algorithm to make one listen and other reply?

    Like

  7. Hello Erich, Thanks you for this tutorial. It was really helpful.
    I want to now something, how do you do to make it?, I mean understand the devices and make a source code.

    Note: I’m a newbie, I really interested in learn how to develop, but I dont know where to begin.

    Like

  8. Hello
    I am very enjoy to have found your web pages
    I am Involved in building automation and home automation.
    My project is visible on speranto.fr (french website).
    For this project i am starting with Freescale KINETIS (Board FRDM-K20D50M ).
    My PLC Will communicate with three support (for now)
    1) 2.4Ghz radio with Nordic nFR24L01P + (NRF24LE1 is more powerfull he have UART serial interface).
    2) Twisted Pair RS485
    3) Encapsulation bus frame inside TCP / IP packets with the Microchip ENC28J60 circuit (which is very cheap, and drived by SPI)
    For nFR24L01P + you have already done a lot of work and it will save me time.
    For RS485, it should not pose much problem.
    For the ENC28J60 circuit I looking if someone has already write code for a KINETIS Freescale.
    Yet all my congratulations for the quality of your explanations.
    Best regards
    Yves Accard

    Like

      • Hello
        Thank you very much for your message.
        I chose the ENC28J60 because it is the first one I found.
        It has existed since 2004 and it is easy to find code “ready to use” (for ATMEGA or MICROCHIP).
        It is easy to find some electronics boards “ready to use”.
        There is also the 244J600, a little more expensive but is a 10/100 Base-T (ENC28J60 is a 10 Base-T).
        In addition, 244J600 may be interfaced in serial mode (and SPI also).
        I watched the W5500 WIZNET, it is 10/100 Base-T , drived by SPI and the price is the same as 244J600.
        I have found only a very little number of electronics boards “ready to use” for use with W5500 (on INTERNET).
        But I find a lot of electronic boards with this component WIZNET W5100 (for Arduino).
        The WIZNET chipset W5100 is quite expensive (5 dollards!).
        In summary, the WIZNET W5500 is more powerful and almost the same price as ENC28J60.
        I am ready to use the W5500 if someone develops some code that I could easily reuse.
        Because I have more expertise in hardware than software .
        Again thank you.
        Yves Accard
        Onebus

        Like

  9. Hi Erich, great tutorial thanks. I’m just wondering if all of this sample code will work without the use of the logic analyser that you have used?

    Like

    • Hi David,
      yes, a logic analyzer is not needed. I only used it to illustrate the signals. But in any case, I recommend a logic analyzer as a tool for every engineer working with hardware: without it, it will be very hard to isolate and solve communication problems with external hardware.

      Like

  10. Pingback: RNet: optional ACK, Dynamic Payload and Extended nRF24L0+ Driver | MCU on Eclipse

  11. may you please tell me do this transceiver be interfaced with mobile’s bluetooth if not is there any low power module which do so ?

    Like

  12. hey is there any a low power bluetooth module having range upto 50 meters, which can be interfaced with my mobile bluetooth.
    and please give me link where i can get that.

    Like

    • Hello,
      50 meters will be very challenging with normal antennas or normal modules availble. I suggest that you make a research and do a comparison of different modules and measure things with a professional equipment. ‘Best low power’ is not a simple thing to evaluate: it will depend on usage, transmittion patterns and even software drivers used. To the end, you need to do your own research on this. I think nobody will be able to provide you a link or a definitive answer.

      Like

  13. Pingback: IoT: FreeRTOS Down to the Micro Amps | MCU on Eclipse

  14. your tutorial helped me in understanding RF24L01. kindly tell me that if we want to create a two way communication between RF then we will uSE the same 8 pins of RF at both ends or their will any change ? basically i want create communication between PIC microcontroller and PC through RF 24L01 , but i dnt know what will be the interfacing circuits between PIC and RF at one end and between RF and PC at the other end … ? kindly help me out

    Like

    • It should be fairly easy to use the nRF24L01+ with your PIC. But on the PC side that will be different, as your PC (windows? Linux?) is not an ideal system for interfacing with such a module. What we did is using it with a Raspberry Pi, and that worked pretty well :-)

      Like

  15. Hello Erich, I have too boards one i config as a Sender and other a receiver. But for sure they arent communicating to each other, because the receiver is just blinking blue.
    How can i debug that?

    thanks

    Like

  16. Great tutorial. Educative and helpfull. I do have a question: is it possible to connect nRF24L01+ (or other) with regular Access Point (with WPA or other protection) on 2.4 GHz? nRF24L01+ would be only transmitter.

    Like

  17. Hello and thanks to every one, I started a new project for wireless object locator (without RFID) using PIC18F452 Microcontroller. what is the best transceiver to use ? I need 4 units to stick them to objects and just to send some signal replaying to some polling mechanism and also to generate alarm when objects are out of some rang. thanks again.

    Like

      • Thanks Erich, well, what I need is a microcontroller-based system within an office or house that locates objects by emitting light and sound signal when they are lost and also warns when these objects go out the building.

        Like

  18. Thanks Erich, well, what I need is a microcontroller-based system within an office or house that locates objects by emitting light and sound signal when they are lost and also warns when these objects go out the building.

    Like

  19. Pingback: Enhanced RNet Wireless Components and Communication Stack | MCU on Eclipse

  20. Pingback: Tutorial: Nordic Semiconductor nRF24L01+ with the Freescale FRDM-K64F Board | MCU on Eclipse

  21. Pingback: Zumo Robot with WiFi and GPS | MCU on Eclipse

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s