EEVblog Electronics Community Forum

Electronics => Projects, Designs, and Technical Stuff => Topic started by: Kalcifer on May 11, 2021, 06:00:48 am

Title: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Kalcifer on May 11, 2021, 06:00:48 am
I have one of those nRF24L01 ~2.4GHz transmitter modules, as seen here:
http://www.hobbyandyou.com/content/images/thumbs/0002081_nrf24l01-24ghz-wireless-transceiver-module-for-arduino.jpeg (http://www.hobbyandyou.com/content/images/thumbs/0002081_nrf24l01-24ghz-wireless-transceiver-module-for-arduino.jpeg).
I have been having some trouble getting it to work (data not being received), so I wanted to make sure that I am, at least, using the right method for configuring the device(s) for use; I want to make sure that I am understanding the datasheet correctly. So, my setup is as follows:

Receiver:
1. Enter RX mode by setting the PWR_UP bit and the PRIM_RX bit, and bringing the CE pin logic high.
2. Default receive address is used at data pipe 0: 0xE7E7E7E7E7
2. Ensure that the data pipe 0 (using the default address that is used at reset) is enabled to receive 1 byte by writing 1 to RX_PW_P0.

Transmitter:
1. Go into standby-2 mode to wait for transmit data by setting the PWR_UP bit, clearing the PRIM_RX bit, and bringing the CE pin logic high.
2. Default transmit address at reset is used: 0xE7E7E7E7E7

Transmission:
1. Write a byte of data to the transmit payload with 0b00000001 written to W_TX_PAYLOAD
2. from my understanding, this should automatically start the transmission of data
3. read the received data by reading from R_RX_PAYLOAD

Please let me know if my configuration and usage method is correct.

Thank you.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Doctorandus_P on May 11, 2021, 07:58:30 pm
My sincerest advise is tho throw the thing in the garbage bin and buy some better hardware.

Years ago I bought a bunch of these things and wrote a driver library from a collection of a bunch of giants before me, but mostly from reading the datasheet about 20 times.
The nRF24L01+ is another chip from the incompatible nRF24L01, and I found the whole thing just horrible.
The designer of that chip had some very distorted ideas of what is useful in such a chip and what is not. They have some "6 pipe" model, which is either not enough or too much, and either way you have to find a way to work around it. It's doing a bunch of buffering and it's very hard to determine what is buffered where and when. I've had the thing lock up, and then after a complete power cyle have it receive messages from before the power cycle. The way their internal synchronization works is very yucky.

I had a short moment to light my interest when I noticed that it had some signal strength measurement, but it's implemented so badly that it's completely useless.

And on top of that. There are some clones on the market and these have some bugs and / or incompatibilities, so you never know what you're working with.

So pay 50ct or a euro more for some other RF breakout board that has some decent hardware on it.

The HopeRF RFM95 and RFM96 are probably much better chips. But probably anything is better than the nRF24 horror.
If you're lucky you can get some "arduino" lib for it to work in 10 minutes or so, but that's because someone else has been banging their heads to the wall for days before you.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Kalcifer on May 12, 2021, 04:23:48 am
My sincerest advise is tho throw the thing in the garbage bin and buy some better hardware.
Perhaps something like an ESP8266 would be good. Those are decently popular it seems. Although, I have looked into it before, and I'm not entirely sure how to go about programming them.

The HopeRF RFM95 and RFM96 are probably much better chips. But probably anything is better than the nRF24 horror.
If you're lucky you can get some "arduino" lib for it to work in 10 minutes or so, but that's because someone else has been banging their heads to the wall for days before you.
I don't have HopeRF chips but I do have a couple of those Semtech SX1278 LoRa module things lying around which seem to be similar in idea to the chips that you mentioned.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on May 12, 2021, 11:22:01 am
Here is the general flow I use.  I have the 'Plus' modules.
There is an Arduino library for the nRF24L01+ (RF24 lib)


// CHANGES TO POWER-ON DEFAULTS:
nRF_CONFIG (00h)     = nRF_CRC1|nRF_PwrDn|nRF_PRX (09h)
nRF_SETUP_RETR (04h) = (ARD=500us, ARC=3 retries) (13h)
nRF_RF_SETUP (06h)   = nRF_1Mbps|nRF_PWR0 (06h)


// RECEIVER SETUP:
nRF_RX_ADDR_P0 (0Ah) = (E7E7E7E7E7h)
nRF_RX_ADDR_P1 (0Bh) = (C2C2C2C2C2h)
nRF_TX_ADDR (10h)    = (E7E7E7E7E7h)
nRF_RX_PW_P0 (11h)   = 8
nRF_RX_PW_P1 (12h)   = 8
// Start listening: Power-up and Receive mode
nRF_CONFIG (00h) = nRF_CRC1|nRF_PwrUp|nRF_PRX (0Bh)
delay_us(130)
// Set CE high
CEpin = 1
// Read FIFO status
while ((nFIFO_STATUS & 0x03) != 1) {
   // read payload, nRF_RD_RX_PL (61h)
}


// TRANSMITER SETUP:
nRF_RX_ADDR_P0 (0Ah) = (C2C2C2C2C2h)
nRF_RX_ADDR_P1 (0Bh) = (E7E7E7E7E7h)
nRF_TX_ADDR (10h)    = (C2C2C2C2C2h)
nRF_RX_PW_P0 (11h)   = 8
nRF_RX_PW_P1 (12h)   = 8
// Write payload: Power-up and Transmit mode
nRF_CONFIG (00h) = nRF_CRC1|nRF_PwrUp|nRF_PTX (0Ah)
delay_us(150)
for (int i=0;i<8;i++) {
   // write payload, nRF_WR_TX_PL (A0h)
}
// Strobe CE
CEpin = 1
delay_us(16)
CEpin = 0
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: SiliconWizard on May 12, 2021, 05:15:18 pm
I've used the nRF24L01+ chip a few years ago, and I don't remember how close to the nRF24L01 it is, nor if your module doesn't actually contain a nRF24L01+, which I'm suspecting.

Anyway, from what I remember, what MarkF showed looks correct to me. If you have any further problem, I may have to take a closer look at my project to give possible additional info.


Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: janoc on May 12, 2021, 06:01:52 pm
nRF24L01+ is the enhanced version of the original nRF24L01. nRF24L01+ can talk to both nRF24L01+ and nRF24L01 but the nRF24L01 doesn't support some data rates that nRF24L01+ does. The original (non-plus) version hasn't been sold for years, so it is unlikely you would find one in a new module these days.

There is nothing wrong with these radios, no need to hate on them. They work, they are simple to use and widely deployed - e.g. many wireless keyboards/mice that use dongles are based on these chips, as are various cheap drones and toys needing remote control.

However, that is assuming you get the genuine one and a good quality module. The stuff from AliExpress and wherever is a lottery.

There are even fakes/clones of these Nordic radios around:
https://hackaday.com/2015/02/23/nordic-nrf24l01-real-vs-fake/

Oh and do make sure you don't accidentally damage them with 5V - there are 3.3V only (the data pins are 5V tolerant, AFAIK). I had a few of these damaged by a dodgy DC-DC converter module that was letting some voltage spikes in and then the radio was locking up, talking to the host but not transmitting and weird and wonderful stuff like that.


My sincerest advise is tho throw the thing in the garbage bin and buy some better hardware.
Perhaps something like an ESP8266 would be good. Those are decently popular it seems. Although, I have looked into it before, and I'm not entirely sure how to go about programming them.

If you don't mind the roughly 10x power consumption and the need to configure wifi ...

If you need low power (which is likely why you were looking at the nRF24L01+ in the first place), then nRF52 or nRF53 series could be a good fit - ARM Cortex core, radio that can handle Bluetooth Low Energy, Zigbee, ANT, Shockburst (the nRF24L01+ protocol) and many other things and still very low power consumption. E.g. the recently released Apple AirTags use nRF52 SoCs.

Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: SiliconWizard on May 12, 2021, 09:30:04 pm
Agreed, those ICs are actually pretty good. Sure they are barebones - that's what they are meant to be. But yes they draw little power. The protocol is pretty raw. But you can implement your own protocol on top of that. I've seen a project implementing simple BLE with them. AFAIR, Nordic also provides a protocol that you can use with them: Gazell.

I don't get why anyone would suggest using WiFi instead (unless you actually want to connect to an existing network). Talk about waste.

You could otherwise consider BLE if you are after a low power solution with a more sophisticated protocol. TI chips, like the CC2640 (IIRC?) contain an ARM Cortex core and are very low power. I think you can implement something else than BLE with those as well if you're so inclined.


Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Doctorandus_P on May 12, 2021, 10:07:33 pm
I don't get why anyone would suggest using WiFi instead...

Nobody suggested to use WiFi.
OP asked if that could be a good alternative.

And I agree with the others. WiFi is power hungry.
A part of the power hungryness is because of encryption and key exchange, which needs relatiely much data going to and fro.
Apparerently you can get some decent battery life out of an ESP8266, if it sleeps 99.999% of it's time and does not much communicate in the time it's awake, but it will never be as low as those small radio's.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Kalcifer on May 12, 2021, 10:28:23 pm
If you need low power (which is likely why you were looking at the nRF24L01+ in the first place), then nRF52 or nRF53 series could be a good fit - ARM Cortex core, radio that can handle Bluetooth Low Energy, Zigbee, ANT, Shockburst (the nRF24L01+ protocol) and many other things and still very low power consumption. E.g. the recently released Apple AirTags use nRF52 SoCs.

Correct, I need decently low power consumption, as well as a somewhat decent range. I need the modules to be able to communicate across a length of my yard (retrieving data from a sensor from about 30 or so meters away. Very little obstructions, only a very slight incline, some shrubs and a fence.). I also really don't want to go through the headache of designing my own antenna and whatnot (that bit of my schooling is a little fuzzy), so the premade module was a good fit for me. Also, I find that really cool that the air tags are using nordic semiconductor chips! I had no idea.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on May 13, 2021, 12:39:29 am
I'm using the nRF24L01+ modules in my wireless model railroad throttles.
There is no problem across my basement at 10 meters.
My guess is you can go 20 meters, but 30+ meters is probably pushing their range for the PCB antenna.

(https://lastminuteengineers.com/wp-content/uploads/arduino/nRF24L01-Wireless-Transceiver-Module.jpg)

There is a high power version with a screw-on antenna that might get you the range you want.

(https://lastminuteengineers.com/wp-content/uploads/arduino/nRF24L01-PA-LNA-External-Antenna-Wireless-Transceiver-Module.jpg)
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: Kalcifer on May 13, 2021, 04:05:39 am
If you need low power (which is likely why you were looking at the nRF24L01+ in the first place), then nRF52 or nRF53 series could be a good fit - ARM Cortex core, radio that can handle Bluetooth Low Energy, Zigbee, ANT, Shockburst (the nRF24L01+ protocol) and many other things and still very low power consumption. E.g. the recently released Apple AirTags use nRF52 SoCs.

Is the nRF24L01 obsolete?  I took a look on nordic's website, and in the nRF24 section, it says "not recommended for new designs" and then lists the nRF52 series chips as alternatives. Would the nRF52 series chips have improvements in range? They use Bluetooth 5.2 bluetooth low energy. Not sure how that stacks up to the performance of the nRF24 series.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: sleemanj on May 13, 2021, 09:32:17 am
My guess is you can go 20 meters, but 30+ meters is probably pushing their range for the PCB antenna.

The PCB antenna is limited as a transmitter, but as a receiver it's pretty decent.

https://www.youtube.com/watch?v=gtM832Z0ujE (https://www.youtube.com/watch?v=gtM832Z0ujE)

It's all about the antenna really you can do some very long distances if there's nothing in the way...

https://www.youtube.com/c/iforce2d/search?query=NRF24L01%2B (https://www.youtube.com/c/iforce2d/search?query=NRF24L01%2B)

https://www.youtube.com/watch?v=4XRp7pkZgPM (https://www.youtube.com/watch?v=4XRp7pkZgPM)
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: janoc on May 13, 2021, 09:56:10 am
Is the nRF24L01 obsolete?  I took a look on nordic's website, and in the nRF24 section, it says "not recommended for new designs" and then lists the nRF52 series chips as alternatives. Would the nRF52 series chips have improvements in range? They use Bluetooth 5.2 bluetooth low energy. Not sure how that stacks up to the performance of the nRF24 series.

nRF24 is not really obsolete (as in end of life/not made anymore) but they would want you to phase it out. It is likely going to be around for a long time, though, a lot of products use these radios.

nRF52 does not use BLE. nRF52 is capable of running BLE but not only - it is a multiprotocol radio and with the exception of Bluetooth Classic and Wifi it can probably handle every common wireless protocol.

Not sure what you mean by "performance of the nRF24 series". Bitrate? That's a function of the protocol, both SoCs have 2Mbps capable radios. Shockburst on nrf24L01+ is 250kbps-2Mbps (AFAIK). That's the same on nRF52.

Power consumption? I had nRF52832 take about 6mA while running the full Bluetooth Low Energy/Bluetooth Mesh stack and actively advertising. When sleeping it is down in uA territory. nRF24L01+ takes about 12mA when transmitting. So that's comparable (the different protocol I have used prevents 1:1 comparison here).

Plus nRF52x is has an ARM core with built-in flash you can run your own programs on, nRF24L01+ requires another micro to act as a host because the SoC isn't user programmable.

Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: janoc on May 13, 2021, 10:15:44 am
Agreed, those ICs are actually pretty good. Sure they are barebones - that's what they are meant to be. But yes they draw little power. The protocol is pretty raw. But you can implement your own protocol on top of that. I've seen a project implementing simple BLE with them. AFAIR, Nordic also provides a protocol that you can use with them: Gazell.

You can run the same Shockburst protocol even on the nRF52, it is compatible and officially supported.

nRF24 can't run BLE, the radio is "hardwired" and is unable to broadcast/receive data in the required format. It was only "abused" to send BLE advertisement packets with a bit of hackery, so it can act as e.g. the iBeacon style tag. Not much else.

nRF52 can run the following (but not all models - the ones with smaller flash can't run everything due to the size of some of the sw stacks):

- Bluetooth Low Energy
- Bluetooth Mesh (based on BLE but not compatibile with it)
- ZigBee
- Thread (IPv6 based mesh network)
- ANT (very low power protocol used mostly in fitness equipment)
- IEEE 802.15.4 (low level protocol, basis for ZigBee, Thread, 6LoWPAN, SNAP, MiWi ...)
- Shockburst and Enhanced Shockburst (same as nRF24L01+)
- Gazell (proprietary Nordic thing)
- NFC (has a separate antenna for it)

Possibly more, because there are some other protocols that are built on top of  Shockburst or IEEE 802.15.4 that can run on these SoCs. Some parts have also USB, so if you need to build a dongle to interface e.g. a mesh network to a PC, these are ideal parts for it (and, in fact, many BLE dongles contain them for this reason).

If someone wants to mess with this, I would strongly recommend looking at the Zephyr project first instead of trying to work with the Nordic SDKs. It is much easier because you don't have to deal with the Nordic RTOS blob ("softdevice"). Zephyr is completely open source so if anything goes haywire (and it will, especially when implementing complex stuff like BLE or some mesh) it is much easier to debug than getting a hard fault from the softdevice and then wondering where in that binary blob it faulted and why.

https://www.zephyrproject.org/ (https://www.zephyrproject.org/)



Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on February 15, 2024, 05:15:55 pm
Hello ...
My name is Tony Truong. I am a beginner and trying to make 2 x nrf24L01+ to communicate with 2 UNO R3.
I follow the code sequence of MarkF (in this forum) to set them up. They don't really work as I expected.

Note: I write code in assembly and using SPInterface with CE on PORTD bit 7, CSN on PORTB bit 0

On receiver side ...
With CE = 0
I set up all parameter and then cfg_reg to 0x0B and delay ms
Then CE = 1
Looping on fifo_status (reg 0x17) .. checking bit 0 if RX is NOT empty ...
Upload code to receiving UNO

On transmitter side ...
With CE = 0 ...
I set up all parameters and the cfg_reg to 0x0A ... delay ms
Writing W_TX_PAYLOAD ... then CE=1 ... delay ms then CE=0
Upload code to transmitting UNO

I saw the data is received by the receiver once.

I did several experiments and observed these strange things:
- Intended data sent to receiver EVEN I put a break before/in front CE=1 ... looks like nrf was in standby-2 mode but data was sent even before CSN de-asserted (W_TX_PAYLOAD writing sequence not completed yet)
- Status reg (addr = 0x07) = 0x1F ... Max num transmit Interrupt ... TXFifo FULL ...
- Fifo_status reg (addr - 0x17) = 0x21 ... TXFifo Full ... RXFifo Empty

- On receiver side .. received all intended data that were sent from  transmitter ... code read all received data out and print on terminal
- Status reg content (addr 0x07) = 0x4E .. RX_DR=1
- CE=0
- Code write 1 to reset RX_DR .... but can not get reset to 0x0E
- Receiver can not receive afterward as this bit is SET

Please help. I can copy and paste assembly code if needed.
Thanks a lot and have a good day.
Tony Truong




Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: janoc on February 15, 2024, 06:00:58 pm
Please don't necropost to 3 years old threads with a completely unrelated issue. Make a new post in the proper section.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on February 15, 2024, 08:34:04 pm
Tony,

I believe you're hanging up because you're not handling any errors. 
At this point, I'll just give you some code snippets from my PIC micro-controller and see if you can figure out the problem before I spend much time on it.

This example code is assuming 8 byte transmit and receive payload data.

nRF library header
Code: [Select]
// nRF24L01+ Commands

#define nRF_RD_REGISTER       0x00
#define nRF_WR_REGISTER       0x20
#define nRF_RD_RX_PL_WID      0x60
#define nRF_RD_RX_PL          0x61
#define nRF_WR_TX_PL          0xA0
#define nRF_WR_ACK_PL         0xA8
#define nRF_W_TX_PL_NOACK     0xB0
#define nRF_FLUSH_TX          0xE1
#define nRF_FLUSH_RX          0xE2
#define nRF_REUSE_TX_PL       0xE3
#define nRF_NOP               0xFF

// nRF24L01+ Registers

#define nRF_CONFIG            0x00  // Configuration Register
#define nRF_EN_AA             0x01  // Enable Auto_Acknowledgement
#define nRF_EN_RXADDR         0x02  // Enable RX Addresses
#define nRF_SETUP_AW          0x03  // Setup Address Width
#define nRF_SETUP_RETR        0x04  // Setup Automatic Retransmission
#define nRF_RF_CH             0x05  // RF Channel
#define nRF_RF_SETUP          0x06  // RF Setup Register
#define nRF_STATUS            0x07  // Status Register
#define nRF_OBSERVE_TX        0x08  // Transmit Observe Register
#define nRF_RPD               0x09  // Receiver Power Detector
#define nRF_RX_ADDR_P0        0x0A  // Receive Address
#define nRF_RX_ADDR_P1        0x0B
#define nRF_RX_ADDR_P2        0x0C
#define nRF_RX_ADDR_P3        0x0D
#define nRF_RX_ADDR_P4        0x0E
#define nRF_RX_ADDR_P5        0x0F
#define nRF_TX_ADDR           0x10  // Transmit Address
#define nRF_RX_PW_P0          0x11  // Payload Length
#define nRF_RX_PW_P1          0x12
#define nRF_RX_PW_P2          0x13
#define nRF_RX_PW_P3          0x14
#define nRF_RX_PW_P4          0x15
#define nRF_RX_PW_P5          0x16
#define nRF_FIFO_STATUS       0x17  // FIFO Status Register
#define nRF_DYNPD             0x1C  // Enable Dynamic Payload Length
#define nRF_FEATURE           0x1D  // Feature Register

// nRF24L01+ Bits

                     // Configuration Bits
#define nRF_CRC0        0x00  // CRC Off
#define nRF_CRC1        0x08  // CRC 1 Byte
#define nRF_CRC2        0x0C  // CRC 2 Bytes
#define nRF_PwrUp       0x02  // Power Up
#define nRF_PwrDn       0x00  // Power Down
#define nRF_PRX         0x01  // RX Mode
#define nRF_PTX         0x00  // TX Mode

                     // Address Width Bits
#define nRF_AW3         0x01  // 3 Byte Address
#define nRF_AW4         0x02  // 4 Byte Address
#define nRF_AW5         0x03  // 5 Byte Address

                     // RF Setup Bits
#define nRF_250Kbps     0x20  // Data Rate 250 Kbps
#define nRF_1Mbps       0x00  //             1 Mbps
#define nRF_2Mbps       0x08  //             2 Mbps
#define nRF_PWR18       0x00  // -18 dBm
#define nRF_PWR12       0x02  // -12 dBm
#define nRF_PWR6        0x04  //  -6 dBm
#define nRF_PWR0        0x06  //   0 dBm

                     // Status Bits
#define nRF_RX_DR       0x40  // Data Ready
#define nRF_TX_DS       0x20  // Data Sent
#define nRF_MAX_RT      0x10  // Max ReTransmit
#define nRF_RX_P_NO     0x0E  // RX Pipe Number
#define nRF_TX_FIFO     0x01  // TX FIFO Full

                     // FIFO Status Bits
#define nRF_TX_REUSE    0x40
#define nRF_TX_FULL     0x20
#define nRF_TX_EMPTY    0x10
#define nRF_RX_FULL     0x02
#define nRF_RX_EMPTY    0x01

                     // Feature Bits
#define nRF_EN_DPL      0x04  // Enable Dynamic Payload Length
#define nRF_EN_ACK_PAY  0x02  // Enable Payload with ACK
#define nRF_EN_DYN_ACK  0x01  // Enable Dynamic ACK


// Prototypes

uint8_t nrfRead(uint8_t *buf, uint8_t cnt);
uint8_t nrfWrite(uint8_t *buf, uint8_t cnt);


nRF library code
Code: [Select]
/*------------------------------------------------------------*/
#define WRITE(c, s)              \
   PIR1bits.SSPIF = 0;           \
   SSPBUF = c;                   \
   while ( !PIR1bits.SSPIF ) {}  \
   s = SSPBUF

#define READ(s)                  \
   PIR1bits.SSPIF = 0;           \
   SSPBUF = 0;                   \
   while ( !PIR1bits.SSPIF ) {}  \
   s = SSPBUF
/*------------------------------------------------------------*/


uint8_t nrfRead(uint8_t *buf, uint8_t cnt)
{
   uint8_t stat;

   nRF_CSNpin = 0;
   WRITE(buf[0], stat);
   for (int i=cnt-1;i>0;i--) {
      READ(buf[i]);
   }
   nRF_CSNpin = 1;
   return(stat);
}

uint8_t nrfWrite(uint8_t *buf, uint8_t cnt)
{
   uint8_t stat;
   uint8_t tmp;

   nRF_CSNpin = 0;
   WRITE(buf[0], stat);
   for (int i=cnt-1;i>0;i--) {
      WRITE(buf[i], tmp);
   }
   nRF_CSNpin = 1;
   return(stat);
}


nRF functions and main loop
Code: [Select]
// Register reads
uint8_t rfAddr[6]=      {nRF_RD_REGISTER|nRF_RX_ADDR_P0, 0, 0, 0, 0, 0};
uint8_t rfAux[6]=       {nRF_RD_REGISTER|nRF_RX_ADDR_P1, 0, 0, 0, 0, 0};
uint8_t rfCh[2]=        {nRF_RD_REGISTER|nRF_RF_CH, 0};
uint8_t rfStatus[2]=    {nRF_RD_REGISTER|nRF_STATUS, 0};
uint8_t rfObserve[2]=   {nRF_RD_REGISTER|nRF_OBSERVE_TX, 0};
uint8_t rfFIFO[2]=      {nRF_RD_REGISTER|nRF_FIFO_STATUS, 0};

//============================================================
void nrfSetup(void)
{
   // Configure the radio

   // Set addresses
   uint8_t rfA0[6]={nRF_WR_REGISTER|nRF_RX_ADDR_P0, 0xd6, 0xd6, 0xd6, 0xd6, 0x11};
   nrfWrite(rfA0,6);
   rfA0[0]=nRF_WR_REGISTER|nRF_TX_ADDR;
   nrfWrite(rfA0,6);
   uint8_t rfA1[6]={nRF_WR_REGISTER|nRF_RX_ADDR_P1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2};
   nrfWrite(rfA1,6);

   // Set payload width
   uint8_t rfPW0[2]={nRF_WR_REGISTER|nRF_RX_PW_P0, 8};   // transmit 8 bytes
   nrfWrite(rfPW0,2);
   uint8_t rfPW1[2]={nRF_WR_REGISTER|nRF_RX_PW_P1, 8};   // receive 8 bytes
   nrfWrite(rfPW1,2);

   // Set Auto_Acknowledgement
   uint8_t rfAA[2]={nRF_WR_REGISTER|nRF_EN_AA, 0x01};
   nrfWrite(rfAA,2);

   // Set RF data rate and power level
   uint8_t rfSetup[2]={nRF_WR_REGISTER|nRF_RF_SETUP, nRF_1Mbps|nRF_PWR12};
   nrfWrite(rfSetup,2);
}

//============================================================
void powerUp(void)
{
   uint8_t rfConfig[2];

   rfConfig[0] = nRF_RD_REGISTER|nRF_CONFIG;
   nrfRead(rfConfig,2);

   rfConfig[0] = nRF_WR_REGISTER|nRF_CONFIG;
   rfConfig[1] = rfConfig[1] | nRF_PwrUp;
   nrfWrite(rfConfig,2);
}

//============================================================
void powerDn(void)
{
   uint8_t rfConfig[2];

   rfConfig[0] = nRF_RD_REGISTER|nRF_CONFIG;
   nrfRead(rfConfig,2);

   rfConfig[0] = nRF_WR_REGISTER|nRF_CONFIG;
   rfConfig[1] = rfConfig[1] & (~nRF_PwrUp);
   nrfWrite(rfConfig,2);
}

//============================================================
void startListening(void)
{
   // Set PwrUp and PRX
   uint8_t rfRcvMode[2]={nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PRX};
   nrfWrite(rfRcvMode,2);

   // Clear RX_DR, TX_DS, MAX_RT bits
   uint8_t rfClear[2]={nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};
   nrfWrite(rfClear,2);

   // Set CE high
   nRF_CEpin=1;
   delay_us(130);
}

//============================================================
void stopListening(void)
{
   // Set CE low
   nRF_CEpin=0;

   // Flush TX FIFO
   uint8_t rfFlush={nRF_FLUSH_TX};
   nrfWrite(&rfFlush,1);
}

//============================================================
void writePayload(void)
{
   uint8_t stat,timeout;

   // Set PwrUp and PTX
   uint8_t rfXmtMode[2]={nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PTX};
   nrfWrite(rfXmtMode,2);
   delay_us(150);

   // Fill payload
   uint8_t rfPayload[9]={nRF_WR_TX_PL};

   // TODO::  Fill in the 8 bytes of data to send here.
   //         rfPayload[1] thru rfPayload[8]

   . . .

   // Send payload
   nrfWrite(rfPayload,9);

   // Strobe CE
   nRF_CEpin=1;
   delay_us(16);
   nRF_CEpin=0;

   // Wait for complete
   timeout=100;
   do {
      delay_ms(1);
      stat = nrfRead(rfObserve,2);
   } while ( (stat & 0x30) == 0 && --timeout > 0 );

   RxReady = (stat >> 6) & 0x01;    // RX_DR
   TxOkay  = (stat >> 5) & 0x01;    // TX_DS
   TxFail  = (stat >> 4) & 0x01;    // MAX_RT

   // Clear RX_DR, TX_DS, MAX_RT bits
   uint8_t rfClear[2]={nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};
   nrfWrite(rfClear,2);

   if (TxFail) {
      uint8_t rfFlush={nRF_FLUSH_TX};
      nrfWrite(&rfFlush,1);
   }
}

//============================================================
void readPayload(void)
{
   uint8_t rfPayload[9]={nRF_RD_RX_PL};
   nrfRead(rfPayload,9);

   // TODO::  Unpack the 8 bytes of data you received here.
   //         rfPayload[1] thru rfPayload[8]

   . . .

}

//============================================================


//============================================================
void main(void)
{
   // Initialization
   nrfSetup();
   startListening();

   // Main loop
   while (1) {

         // Read FIFO status                                >>>>> Status <<<<<
         stat = nrfRead(rfFIFO,2);
         TxFIFO = (rfFIFO[1] >> 4) & 0x03;
         RxFIFO = (rfFIFO[1]) & 0x03;

         RxReady = (stat >> 6) & 0x01;    // RX_DR
         TxOkay  = (stat >> 5) & 0x01;    // TX_DS
         TxFail  = (stat >> 4) & 0x01;    // MAX_RT

         // Read available payloads                         <<<<< Payload <<<<<
         while ( (rfFIFO[1] & 0x01) != 1 )
         {
            readPayload();          // Get payload
            nrfRead(rfFIFO,2);      // Get FIFO status
         }

         // Write payload                                   >>>>> Payload >>>>>
         if ( anything_to_send )
         {
            stopListening();
            writePayload();         // Send payload
            startListening();
         }

         // 10Hz processing loop
         delay_ms(100);
   }
}
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on February 17, 2024, 05:33:49 pm
Thanks a lot MarkF.
I am reading/working on your snippets and let you know how far I can achieve.
I will have certainly to ask more for your helps along the way ... but right now have to digest your code skeleton and proceed.
Thanks again and have a good day.
Tony Truong 
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on February 19, 2024, 02:14:40 am
Hi MarkF ...
I tried to follow your snippets but get stuck at these sequences:
- In Write Payload ... 
  do {
      delay_ms(1);
      stat = nrfRead(rfObserve,2);
   } while ( (stat & 0x30) == 0 && --timeout > 0 );

   RxReady = (stat >> 6) & 0x01;    // RX_DR.  ???
   TxOkay  = (stat >> 5) & 0x01;    // TX_DS.   ???
   TxFail  = (stat >> 4) & 0x01;    // MAX_RT.    ???

   // Clear RX_DR, TX_DS, MAX_RT bits
   uint8_t rfClear[2]={nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};
   nrfWrite(rfClear,2);

AND
 In main loop ...
// Main loop
   while (1) {

         // Read FIFO status                                >>>>> Status <<<<<
         stat = nrfRead(rfFIFO,2);
         TxFIFO = (rfFIFO[1] >> 4) & 0x03;
         RxFIFO = (rfFIFO[1]) & 0x03;

         RxReady = (stat >> 6) & 0x01;    // RX_DR. ???
         TxOkay  = (stat >> 5) & 0x01;    // TX_DS.  ???
         TxFail  = (stat >> 4) & 0x01;    // MAX_RT.   ???

         // Read available payloads                         <<<<< Payload <<<<<
         while ( (rfFIFO[1] & 0x01) != 1 )
         {
            readPayload();          // Get payload
            nrfRead(rfFIFO,2);      // Get FIFO status
         }

What are these variables and where in the code they are used/called RxReady, TXOkay, TxFIFO, and RxFIFO?
I thought nRF_Status register has these bits RX_DR, TX_DS, and MAX_RT, not in _Observe one.

Also, I have not seen where the receiver "Clear" the RX_Dr (Status @ 0x07) bit in the code in the "main loop".

Could you please provide more info on these? I really appreciate your insights and helps.
Thanks a lot MarkF. Have a nice day.
Tony Truong


Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on February 19, 2024, 08:51:14 pm
Hi MarkF ...
I tried to follow your snippets but get stuck at these sequences:
In Write Payload ... 

   do {
      delay_ms(1);
      stat = nrfRead(rfObserve,2);
   } while ( (stat & 0x30) == 0 && --timeout > 0 );

   RxReady = (stat >> 6) & 0x01;    // RX_DR
   TxOkay  = (stat >> 5) & 0x01;    // TX_DS
   TxFail  = (stat >> 4) & 0x01;    // MAX_RT

In main loop ...

   // Read FIFO status
   stat = nrfRead(rfFIFO,2);
   TxFIFO = (rfFIFO[1] >> 4) & 0x03;
   RxFIFO = (rfFIFO[1]) & 0x03;

   RxReady = (stat >> 6) & 0x01;    // RX_DR
   TxOkay  = (stat >> 5) & 0x01;    // TX_DS
   TxFail  = (stat >> 4) & 0x01;    // MAX_RT

What are these variables and where in the code they are used/called RxReady, TXOkay, TxFIFO, and RxFIFO?
I thought nRF_Status register has these bits RX_DR, TX_DS, and MAX_RT, not in _Observe one.

The comments tell the story here.  The variable names are only meaningful in the program I pulled this code from.  They are not used in communicating with the nRF24L01+ transceiver.  This code was pulled from wireless hand-held throttles for my model railroad.  The variable are used as radio status indicators at the top of my display.  Hard to see in this photo but they are the five dots at the very top of the screen:

   (https://www.eevblog.com/forum/chat/model-trains/?action=dlattach;attach=1727837;image)

The nRF status register is returned for ALL command words.
You don't have to explicitly read the status register.

   [attachimg=1 width=700]

Quote
In Write Payload ... 

   // Clear RX_DR, TX_DS, MAX_RT bits
   uint8_t rfClear[2]={nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};
   nrfWrite(rfClear,2);

   if (TxFail) {
      uint8_t rfFlush={nRF_FLUSH_TX};
      nrfWrite(&rfFlush,1);
   }

Also, I have not seen where the receiver "Clear" the RX_Dr (Status @ 0x07) bit in the code in the "main loop".
Could you please provide more info on these? I really appreciate your insights and helps.

I'm not sure I understand your question.  I don't reset/clear anything in the main loop that I know of.

The only time I clear anything is at the end of the 'Write Payload' function.  In this case after the transfer is complete, I clear all the status bits.  I'm not sure it needs to be done for the nRF to function.  More so, just to get a status for display on the screen and they need to be cleared before the next transmission.  During my initial design, it was hard to determine what was going on with the nRF.  Just leftover debugging that's mostly used to see if the nRF is hungup.

As far as clearing the RX_DR when doing the transmission, I just cleared everything at the same time.  The code generally depends on the state in the FIFO status register instead of the RX_DR status bit when determining if data is available.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on February 19, 2024, 09:25:42 pm
As I mentioned, some of the code relates to debugging.
You will reach a point where you will want to know how many message transfers fail.
Even with the Enhanced ShockBurst™, you will have some level of packet loss.
It's hard to determine this packet loss.  In some of my critical messages, I added a sequence number.

Therefore, I recommend thinking about debugging and eventual having some kind of status/health display of the nRF nodes.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on February 20, 2024, 03:46:19 pm
Great thanks to MarkF.
I have rewrote/corrected my assembly code based on your guidance and luckily it is working, just for now, with a simple "1-way" repeated message.
You were right on nRF's Status returns automatically when UNO's SPI clocks out the command, either for a Read or a Write.
You are really a great teacher. I will certainly continue my own experiments and appreciate your helps in the future.
I live in Austin, TX and if possible I really want to meet and learn from you.
Thanks again and have a great day.
Tony Truong
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on February 20, 2024, 04:18:53 pm
Glad I could help.

One additional thing that may be useful in your design:

I'm actually using one pipe with 'EN_AA Enhanced ShockBurst™' enabled and another that is not enabled.
The enabled pipe will re-transmit the set number of times when packet loss is detected (think TCP/IP in a network connection).  The other pipe doesn't not even try to detect packet loss (think UDP).

What this does is that that the ShockBurst™' pipe would be your critical pipe.  While the other pipe can 'broadcast' to multiple nRF transceivers.  In my model railroad scenario, my laptop receives the throttle position from multiple wireless units (i.e. the ShockBurst™' pipe) trying to error correct each message for dropped packets.  In turn, the laptop transmits on the other pipe to all the units info needed by each without the need to specifically send the same message multiple times.  That is because there is no response from the receiver when ShockBurst™' is turned off.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on March 04, 2024, 05:06:46 pm
Good morning MarkF.
I have been using a very simple code from How to Mechatronics "Dejan Nedelkovski, www.HowToMechatronics.com (http://www.HowToMechatronics.com)"
I have tried for more than 2 weeks, with a lots of combinations, to make 2 x nRF24L01 transceivers to communicate both way without success.
It only works in 1-way direction and a very specific way.
After having interchanged nRFs and it did NOT work, I bought another UNO(3). Same result.
Note1: My laptop assigned automatically COM(x) to UNO(y) at the first time plugged in and keep the same COMx when re-plugged in for used
Note2: Thinking it might be the nRF so I have interchanged nRFs but the result was the same.
It sounds like the transmission is only working with the elder UNO. The younger UNO in nRF pair can only receive.
 
Below is the truth table:

Combination Table               
               
UNO-1   UNO-2   UNO-3        TX-Code in                RX-Code in             Results
               
COM-3   COM-4   No-used   UNO-1 on COM-3   UNO-2 on COM-4   Working fine
COM-3   COM-4   No-used   UNO-2 on COM-4   UNO-1 on COM-3   Not working
               
COM-3   No-used   COM-5   UNO-1 on COM-3   UNO-3 on COM-5   Working fine
COM-3   No-used   COM-5   UNO-3 on COM-5   UNO-1 on COM-3   Not working
               
No-used   COM-4   COM-5   UNO-2 on COM-4   UNO-3 on COM-5   Working fine
No-used   COM-4   COM-5   UNO-3 on COM-5   UNO-2 on COM-4   Not working

Not sure what I have missed. I don't think USB/COM ports have effects on UNO-nRF code and functions but really I have no clue here.
Hoping you can shed the light on this.
Thanks a lot
Tony Truong
 
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on March 04, 2024, 08:05:24 pm
The Arduino ports are not that important as long as you can upload your program to the Uno and see the serial monitor.

What is of particular interest is how you configured each nRF.
Of particular interest are registers (leaving the rest at their defaults):
 - EN_AA Enhanced ShockBurst (01h)
 - EN_RXADDR (02h)
 - SETUP_AW (03h)
 - RX_ADDR_P0 (0Ah)
 - RX_ADDR_P1 (0Bh)
 - TX_ADDR (10h)
 - RX_PW_P0 (11h)
 - RX_PW_P1 (12h)

Pay close attention to the byte order of the address registers.  I had mine reversed for a while.

Also, have you looked at the RF24 library and its examples for the Arduino:
   https://github.com/nRF24/RF24

Beyond that, you will need to show me some code.  By that I mean 'pseudo code' !!!
I stopped doing assembly code back in the '80s and I don't do it anymore except for very very special cases where it's required.
IMHO it's just too hard to read and maintain across all the various platforms and processors.
(Please use the forum code tag)
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on March 04, 2024, 09:48:34 pm
Thanks for a quick reply MarkF.
As had trouble to make it to work with my assembly code, I have restarted to test with the simple code from "How to Mechatronis" from Mr. Dejan Nedelkovski's code to verify/understand the reason why.
Unfortunately it behaves the same way, only in direction from elder UNO to younger UNO as I have described in the previous post.
Here is the code ...
Note: Mr. Dejean Nedelkovski uses "Arduino Mega + nRF24L01" and "Arduino UNO/Nano + nRF24L01". I think the code is common in all platform.
Thank as lot.
Tony Truong

======
Transmitter Code
/*
* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
*               
* by Dejan Nedelkovski, www.HowToMechatronics.com (http://www.HowToMechatronics.com)
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/ (https://github.com/tmrh20/RF24/)
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  delay(1000);
}

=====
Receiver Code
/*
* Arduino Wireless Communication Tutorial
*       Example 1 - Receiver Code
*               
* by Dejan Nedelkovski, www.HowToMechatronics.com (http://www.HowToMechatronics.com)
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/ (https://github.com/tmrh20/RF24/)
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}
Code language: Arduino (arduino)
=====

Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on March 04, 2024, 11:18:44 pm
What I have done is at a much lower level talking to the nRF transceiver directly without any libraries.

I leave it to you to debug Mr. Nedelkovski's code. 
At a glance, I wouldn't think the address for both radios should be the same
   ( i.e.  const byte address[6] = "00001"; )

I would start with the 'GettingStarted.ino' example in the RF24 example section.
I spent a lot of time with the RF24 library and never really figured out how they did their addressing. 

The nRF24L01 expects pipe #0 receive address and the transmit address to always be the same.
I then used pipe #1 as the receive of the other transceiver.  For example:

  nRF #1
     RX_ADDR_P0 = E7E7E7E7E7h
     RX_ADDR_P1 = C2C2C2C2C2h
     TX_ADDR    = E7E7E7E7E7h

  nRF #2
     RX_ADDR_P0 = C2C2C2C2C2h
     RX_ADDR_P1 = E7E7E7E7E7h
     TX_ADDR    = C2C2C2C2C2h

The Arduino RF24 library hides the real addressing from you.


P.S.  Please use the code tag when posting code (i.e. the '#' button above the edit box.)
      Or enclose your code within the html like "code", "/code" within square brackets.
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: tonytruong on March 07, 2024, 01:23:02 pm
Good morning MarkF.
Thank you very much for your helps.
I have followed your suggestions and I have successfully made 2 x nRFs talk to each other with "assembly code" now.
I really appreciate your insights and your "open mind" to help out people.
Have a good day.
Tony Truong
Title: Re: Setting Up the nRF24L01 for Transmitting, and Receiving
Post by: MarkF on March 07, 2024, 06:31:48 pm
I've been doing a little experimenting myself.
I was wrong about the addressing needing to be different.
Two nRF24L01+ transceivers addressed and configured exactly the same will communicate with each other using pipe 0.