EEVblog Electronics Community Forum

Electronics => Projects, Designs, and Technical Stuff => Topic started by: Kalcifer on May 30, 2021, 04:19:37 am

Title: nRF24L01 Woes
Post by: Kalcifer on May 30, 2021, 04:19:37 am
I have been banging my head for far too long with this problem, and I am now thoroughly stumped. I am simply trying to get communication to happen between two nRF24 modules. The modules are seemingly configured properly but just refuse to communicate with eachother.

At first, I was working with an ATmega328P directly (The bulk of its code shown below):
Code: [Select]
#include <avr/io.h>
#include <util/delay.h>

#include <string.h>

#include "nrf24l01.h"
#include "spi.h"
#include "uart.h"



#define F_CPU 8000000 // For the _delay_ms() function



void rf_init ( void ); // Initialize the nRF24L01 module.
uint8_t *tx_transfer ( uint8_t *data_frame, uint8_t number_of_payload_bytes );
uint8_t *rx_transfer ( uint8_t *data_frame, uint8_t number_of_payload_bytes );

//uint8_t *received_data;


void main ( void )
{
spi_init();
uart_init();
rf_init();

DDRC &=~ (1 << DDC5); // Set the test "button" pin as an input.
DDRC |= (1 << DDC4); // Set the test "light" pin as an output.

/* Set up the receiver */
// Enter RX mode by setting PWR_UP = 1, PRIM_RX = 1, and CE = high.
rx_transfer( (uint8_t []){ (W_REGISTER | RX_PW_P0), 1 }, 1);
rx_transfer( (uint8_t []){ (W_REGISTER | CONFIG), ((1 << PWR_UP) | (1 << PRIM_RX)) }, 1 );
PORTD |= (1 << PORTD7);
_delay_ms(1); // Wait for the setup to finish.

/* Set up the transmitter */
/* Go into "standby-2" mode. In this mode the transmitter will wait for tx data and transmit it when it receives the data. */
// Enter TX mode by setting PRIM_RX = 0, PWR_UP = 1, and bringing CE high.
tx_transfer( (uint8_t []){ (W_REGISTER | CONFIG), ((1 << PWR_UP) | (0 << PRIM_RX)) }, 1 );
tx_transfer( (uint8_t []){ (W_REGISTER | RF_SETUP), ((0 << RF_PWR_LOW) | (0 << RF_PWR_HIGH)) }, 1 ); // For initial testing purposes, go into low power mode.
PORTD |= (1 << PORTD5);
_delay_ms(1);

while ( 1 )
{
if ( PINC & (1 << PINC5) )
{
tx_transfer( (uint8_t []){ (W_TX_PAYLOAD), 0b00000001 }, 1 );
_delay_ms(1);
uint8_t *received_data = rx_transfer( (uint8_t []){ (R_RX_PAYLOAD) }, 1);
if (received_data[2] == 0b00000001)
{
PORTC ^= (1 << PORTC4);
}
while ( PINC & (1 << PINC5) );
}
}
}


void rf_init( void )
{
DDRD |= (1 << DDD5); // TX Chip Enable (CE)
//PORTD |= (1 << PORTD5); // Enable the transmitter (and receiver) (See Table 1 [2]) by default *Don't enable by default*

DDRD |= (1 << DDD7); // RX Chip Enable (CE)
//PORTD |= (1 << PORTD7); // Enable the receiver (and transmitter) (See Table 1 [2]) by default *Don't enable by default

_delay_ms(100); // Give the module time to boot up (See Figure 3 [2])
}

uint8_t *tx_transfer( uint8_t *data_frame, uint8_t number_of_payload_bytes ) // number_of_bytes_to_transfer -> either bytes to send or bytes to receive.
{
uint8_t return_data_frame[2 + number_of_payload_bytes]; // Create an array to store the data received by the module.
// Position 0 is for the amount of data in the data frame, position 1 is for the status register, and the rest is for the transferred data.
return_data_frame[0] = number_of_payload_bytes + 1; // Ensures that there is always a data spot for the status register.

PORTD &=~ (1 << PORTD6); // Bring the TX slave select line low to initiate the data transfer.
for ( uint8_t transfer_byte = 0; transfer_byte < (number_of_payload_bytes + 1); transfer_byte++ ) // Always make sure that you transfer at least one byte for the status register/command word.
{
return_data_frame[1 + transfer_byte] = spi_transfer(data_frame[transfer_byte]); // Data must be placed after the first position in the array, as the first position is used to store the number of bytes in the array.
}
PORTD |= (1 << PORTD6); // Bring the TX slave select line high to complete the data transfer.

return return_data_frame;
}

uint8_t *rx_transfer( uint8_t *data_frame, uint8_t number_of_payload_bytes ) // number_of_bytes_to_transfer -> either bytes to send or bytes to receive.
{
uint8_t return_data_frame[2 + number_of_payload_bytes]; // Create an array to store the data received by the module.
// Position 0 is for the amount of data in the data frame, position 1 is for the status register, and the rest is for the transferred data.
return_data_frame[0] = number_of_payload_bytes + 1; // Ensures that there is always a data spot for the status register.

PORTB &=~ (1 << PORTB0); // Bring the TX slave select line low to initiate the data transfer.
for ( uint8_t transfer_byte = 0; transfer_byte < (number_of_payload_bytes + 1); transfer_byte++ ) // Always make sure that you transfer at least one byte for the status register/command word.
{
return_data_frame[1 + transfer_byte] = spi_transfer(data_frame[transfer_byte]); // Data must be placed after the first position in the array, as the first position is used to store the number of bytes in the array.
}
PORTB |= (1 << PORTB0); // Bring the TX slave select line high to complete the data transfer.

return return_data_frame;
}



All its doing is trying to initiate communication between two nRF24 modules connected to the ATmega328P. I know that they are being configured, as I am able to see the SPI communication happening on my oscilloscope, and I can verify that the registers which I intended to modify are being modified. But of course, no communication was happening, so I made another post here (https://www.eevblog.com/forum/projects/setting-up-the-nrf24l01-for-transmitting-and-receiving/msg3568493/#msg3568493 (https://www.eevblog.com/forum/projects/setting-up-the-nrf24l01-for-transmitting-and-receiving/msg3568493/#msg3568493)) hoping to get confirmation that my method was correct, but I received little useful feedback for my issue.

Since I suspect my issue had to do with how I was configuring the modules, I decided to take another approach to test them using an arduino, as there is an RF24 library that will handle most of the manual configuration, which I was doing before, automatically. So, I connected the modules to an Arduino Uno in the same configuration as they were with the AVR and wrote the following sketch:
Code: [Select]
#include <SPI.h>

#include <nRF24L01.h>
#include <printf.h>
#include <RF24.h>
#include <RF24_config.h>

#define CHANNEL 100

RF24 reciever(7,8); //CE, CSN
RF24 transmitter(5,6);//CE, CSN
const byte address[6] = "00001";



void setup()
{
  while (!Serial);
  Serial.begin(9600);
 
  reciever.begin();
  reciever.openReadingPipe(0, address);
  reciever.startListening();
  reciever.setChannel(CHANNEL);
  //reciever.setAutoAck(false);

  transmitter.begin();
  transmitter.openWritingPipe(address);
  transmitter.stopListening();
  transmitter.setPALevel(RF24_PA_MIN);
  transmitter.setChannel(CHANNEL);
  //transmitter.setAutoAck(false);
}

void loop()
{
  const char text[] = "hello there";
  transmitter.write(&text, sizeof(text));

   delay(1);
 
  if (reciever.available())
  {
    char text[32] = {0};
    reciever.read(&text, sizeof(text));
    Serial.println(CHANNEL);
    Serial.println(text);
  }
  delay(1);
}


The idea with this was to constantly transmit a string of data, and I was hoping to see that data on the serial monitor, but of course nothing was showing up, And I verified that nothing was being received by checking the SPI data, and of course saw that the receive payload was empty.

I went down the list of trying as many things as I could: I changed timings, I changed channels, I added capacitors across the power and ground pins, I switched out nRF24 modules for others in case these were fried, all to no avail.

I have exhausted every troubleshooting point that I can think of, so I am well and truly stumped with this, and I would love some help.

Thank you.
Title: Re: nRF24L01 Woes
Post by: Doctorandus_P on May 30, 2021, 07:22:49 am
I find it a horrible chip.

It starts with that it's unlikely that you have a nRF24L01, but an nRF24L01+ instead (yep with the plus it's another chip.

I also find the 6-pipeline model extemely flawed and difficult to work around.
There are also some chinese clones with small incompatibilities.
It can work on some power levels, but does not have a decent way to measure input power to make an estimation of output power to be used.

And on top of all that, It's quite possible that your 2.4GHz band is so congested with WiFi that you cant get signals through on most of the channels.

Therefore I advise to use some other RF chip, that costs 20ct more but works properly.


(This thread will probably get some responses that the thing can be beat into submission, and indeed it can, but that does not make it a less horrible chip to work with).
Title: Re: nRF24L01 Woes
Post by: MarkF on May 30, 2021, 11:34:55 pm
Have you seen this other thread?    :-//

https://www.eevblog.com/forum/projects/setting-up-the-nrf24l01-for-transmitting-and-receiving/msg3567950/#msg3567950 (https://www.eevblog.com/forum/projects/setting-up-the-nrf24l01-for-transmitting-and-receiving/msg3567950/#msg3567950)

You NEED to actually setup each nRF24L01+.
Their power-up initialization does NOT set the pipe addresses properly for communications.
Try following the procedures listed for the receiver and transmitter.    |O

Neither of your code snippets initialize and setup the nRF's.  You just go straight into trying to send messages.

F.Y.I.
  I pulled snippets of code from the Arduino library RF24 for my own 'so-called' library (just functions for use in my PIC code).
  If I'm not mistaken, there are working Arduino examples using the RF24 library you can try.  (Google is your friend.)


I find it a horrible chip.
 . . .
Yes.  We all know how much you hate this transceiver.   :=\  :clap:
It doesn't help the OP get it working.
Title: Re: nRF24L01 Woes
Post by: Kalcifer on June 05, 2021, 08:50:12 pm
You NEED to actually setup each nRF24L01+.
Their power-up initialization does NOT set the pipe addresses properly for communications.
Try following the procedures listed for the receiver and transmitter.    |O
So, I took a look at the nrf setup in the thread that you sent, and the only difference that I noticed is that you manually set the addresses correct?
If so, I don't see why there is a need to set the addresses manually. According to the datasheet, the reset value for RX_ADDR_P0 is E7E7E7E7E7h same as the reset value for TX_ADDR. Do the addresses in reality not reset to this value?
Title: Re: nRF24L01 Woes
Post by: MarkF on June 06, 2021, 12:05:36 am
You NEED to actually setup each nRF24L01+.
Their power-up initialization does NOT set the pipe addresses properly for communications.
Try following the procedures listed for the receiver and transmitter.    |O
So, I took a look at the nrf setup in the thread that you sent, and the only difference that I noticed is that you manually set the addresses correct?
If so, I don't see why there is a need to set the addresses manually. According to the datasheet, the reset value for RX_ADDR_P0 is E7E7E7E7E7h same as the reset value for TX_ADDR. Do the addresses in reality not reset to this value?

Both nRF's CAN NOT have the same addressing!!!  You have to change at least one of them.

1)  If you notice nRF #1 addressing points to nRF #2 and nRF #2 addressing points back to nRF #1.
    nRF #1 RX pipe 0 and TX point to nRF #2 RX pipe 1 and
    nRF #2 RX pipe 0 and TX point to nRF #1 RX pipe 1.
    (i.e. pipe's 0 and 1 addresses for nRF #1 are swapped for nRF #2)

2)  RX pipe 0 and the TX pipe addresses must be the same.  At least to have Shockburst work.

3)  Take particular note, the LSByte must be sent out first.  I mistakenly was sending the MSByte first in the beginning.
Title: Re: nRF24L01 Woes
Post by: Kalcifer on June 06, 2021, 03:16:48 am
Both nRF's CAN NOT have the same addressing!!!  You have to change at least one of them.

1)  If you notice nRF #1 addressing points to nRF #2 and nRF #2 addressing points back to nRF #1.
    nRF #1 RX pipe 0 and TX point to nRF #2 RX pipe 1 and
    nRF #2 RX pipe 0 and TX point to nRF #1 RX pipe 1.
    (i.e. pipe's 0 and 1 addresses for nRF #1 are swapped for nRF #2)

Please bear with me if this should all be obvious, I just want to break it down and simplify it to make sure that I am understanding, correctly, what you are saying.

Okay, so, lets simplify your example further to only simplex communication (as it seems that you are referring to half duplex). So, let's say nRF1 is the transmitter, and nRF2 is the receiver.

nRF2 (Receiver)
1. Listen on data pipe 0 by setting ERX_P0 (By default, this is set at reset (along with data pipe 1), according to the datasheet, so I will not change it)
2. Make sure that the data pipe 0 has a unique address at RX_ADDR_P0 (By default, at reset, this is set to 0xE7E7E7E7E7).
3. Set the number of bytes allowed in the RX payload of data pipe 0 at RX_PW_P0 (zero at reset). I set this to be 1 single byte.

nRF1 (Transmitter)
1. Set the address of the data pipe to transmit to at TX_ADDR (By default, at reset, this is 0xE7E7E7E7E7)

From where my understanding is, the only register that needs to be adjusted at reset is RX_PW_P0. Please tell me where my procedure, and understanding is differing from yours, and again, I apologize if it seems obvious to you. It seems to me that for how I have set this up for simplex, it matches what you said in your (2).
Title: Re: nRF24L01 Woes
Post by: MarkF on June 06, 2021, 10:58:45 am
My example describes full-duplex operation.

Each nRF's RX Pipe 0 address = TX pipe address.

// Address setup repeated on each nRF
nRF_RX_ADDR_P0  =  nRF_TX_ADDR  =  'address_of_nRF_you_wish_to_send_message_to'
nRF_RX_ADDR_P1  =  'address_of_nRF_to_receive_messages_from'
nRF_RX_ADDR_P2  =  'address_of_nRF_to_receive_messages_from'
nRF_RX_ADDR_P3  =  'address_of_nRF_to_receive_messages_from'
nRF_RX_ADDR_P4  =  'address_of_nRF_to_receive_messages_from'
nRF_RX_ADDR_P5  =  'address_of_nRF_to_receive_messages_from'

Pipe 0 on both nRF's is part of the transmit operation. 
Pipe 0 is NOT used to receive messages, except in a special case.  Pipes 1 thru 5 receive all the messages.
In Shockburst mode, pipe 0 receives the ACKnowledge from the nRF that received the message.
The transmitting nRF's RX pipe 0 address and TX address point to the pipe 1 address of the receiving nRF.

Pipe 0 on both nRF's is part of the transmit acknowledge sequence.
Pipe 1 on both nRF's receives message from the other nRF.

The transmitting nRF sends messages to the 2nd nRF's pipe 1.

Shockburst mode:

Re-read the nRF24L01+ specification!  No joke.
I've re-read the spec several times a day for two weeks straight and I'm still confused by its addressing.
At least follow my example addressing verbatim to get something working.  Then,  you can experiment to understand it.

Note,  If you're using the Arduino RF24 library, all this addressing is hidden from you.
       The RF24 library has Arduino examples.