This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF24L01+ SPI Communication with NRF52 DK

Hi, I'm trying to use SPI to control an RF module to send audio data from one microcontroller to another. The parts and software I'm working with are:

I need one NRF52 to control an NRF24L01+ to act as a transmitter sending data and the other acting as a receiver; these roles will never need to change and this will be the only NRF24L01+ pair for this project. I've edited the NRF24 libraries from Github to use Nordic's functions instead of Arduino's to avoid having to include everything Arduino (changed delay(#) to nrf_delay_ms(#), using nrf SPI driver instead of Arduino's SPI, etc.). I am having problems getting the two devices to actually communicate with one another despite getting them to (seemingly) work on their own individually. The transmitter says it is transmitting, the receiver says it is waiting to receive, so I believe the initialization to be incorrect on one or both ends. For now all I care about is getting any data across and I can handle getting it up to speed and sending audio files later.
Strange things I've noticed thus far:

  • SPI with the NRF24L01+ only seems to work at 1Mbps
  • In order to not get garbage over SPI I needed to send a NOP first during initialization and then proceed as normal
  • Datasheet mentions the contents of the STATUS register are sent out whenever SS goes from high to low, seemed to be fixed by condensing as many SPI commands into single transmissions instead of using separate enable/ disable commands for the GPIO
  • Writing over the default address in either receive data pipes 0 or 1 did not seem to change their values, they output the same before and after (0xE7E7E7E7E7 for data pipe 0 and 0xC2C2C2C2C2 for data pipe 1).

Here's the print outs I'm getting from my receiver and transmitter test code so far (the data is all in hex with SPI's MOSI in the left column and MISO in the right column):

RECEIVER
Init
FF	00
Config setup
20	07
02	0E
RF setup
26	07
08	00
Set channel to #1
25	0E
01	00
Set Rx Mode
20	0E
03	00
27	0E
70	00
read address pipe line 1
0B	0E
FF	C2
FF	C2
FF	C2
FF	C2
FF	C2
	address: C2 C2 C2 C2 C2
writing address hopefully
2B	0E
41	07
6C	07
6D	07
61	07
6F	07
read address again
0B	07
41	C2
6C	C2
6D	C2
61	C2
6F	C2
	address: C2 C2 C2 C2 C2
read payload if available
17	0E
FF	11
read payload if available
17	0E
FF	11
 
TRANSMITTER
Init
FF	00
Config setup
20	07
02	2E
RF setup
26	07
08	00
Set channel to #1
25	2E
01	00
Flush TX
E1	2E
Write payload
A0	07
4E	2E
6F	00
72	00
64	00
69	00
63	00
00	00
Set to TX mode
20	07
02	00
27	2E
70	00
Flush TX
E1	2E
Write payload
A0	07
4E	2E
6F	00
72	00
64	00
69	00
63	00
00	00
Set to TX mode
20	07
02	00
27	2E
70	00
Flush TX
E1	2E

I'm initializing SPI with:

static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);

/* ... */

void spi_init()
{
    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;

    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
    spi_config.frequency = NRF_DRV_SPI_FREQ_1M;

    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}

Are there any libraries or methods I could be using to more easily interface with the NRF24L01+'s than reusing an Arduino library? I'm going to continue experimenting with SPI commands, timings, ordering, and initialization to see if I can get it working on my own for the time being unless I can find something better.

Parents
  • Hi Andy

    A long time ago I ported the nrf_hal library from the nRFgo SDK (for the nRF24LE1 and the nRF24LU1+) to the nRF5 family, to allow an nRF24L01+ module to be controlled from an nRF51822 or nRF52832 DK. 

    This is by no means an official example, but I can provide it "as is". Just use it at your own risk ;)

    l01_test_app.zip

    The example was made for the nRF5 SDK v13.x.x, and you need to put the project folder into one of the examples/xxx sub folder in the SDK to compile it.

    Best regards
    Torbjørn

  • Hello Ovrebekk,
    At some time between our messages here I managed to get the NRF52 DK and NRF24L01+ to communicate with each other more as well as send data from one NRF24L01+ to another from more experimentation but it's still not great. The biggest issue I was having was an initialization problem: I needed to also set assign a matching payload width for the incoming data on the receiver side. Once I implemented that my devices were successfully sending/ receiving data, but were still limited in that I needed to write NOP commands around the code, namely first during init and once just before flushing the Rx FIFO.
    The weirdest thing I'm running into right now is a seemingly timing related hardfault caused on the receiver side when trying to speed communication up. I ordinarily have printf statements in my NRF24.c to ensure I'm both writing and receiving the correct data over SPI but when removed my receiver NRF52 DK would crash whenever I receive a message unless I include a delay of > ~65 microseconds. I'm not sure why this is the case, and changing around IRQ priorities in the config file hasn't solved this like it normally does when I encounter this sort of problem. Also at some point my NRF24L01+'s started working at SPI frequencies other than 1Mbps, so I've just boosted it up to 8Mbps; I am not sure why SPI is behaving better now but I won't complain.
    Thanks for the .zip, I've downloaded it and am looking through the code and definitely seems far more thoroughly involved than mine at a glance. I'll try importing your code into my SDK project still using Segger Embedded Studio and using it to control both sides to see what improvements I can get from it. Most important to me is 1) not crashing and 2) having the higher SPI speed to minimize time spent communicating with the NRF24L01+'s for future needs.
    Thanks again, I'll update here what I end up going with and how it works out when I've tested everything more!

  • Another update on this:
    I have it partially working now, much to my surprise! In order to actually transmit data I had to disable CRC and auto ACK on both the receiver and transmitter modules unfortunately. Once this was done, however, data transmission has been pretty effective and I've hardly noticed any dropped or incorrect packets regardless of the lacking ACKs and CRC. I've ranged tested the modules a bit as well and at the furthest distance I'm reasonably expecting them to be used at the lowest radio strength (-18dBm) starts missing packets when a person moves their bodies near the receiver. When tested at the highest strength (0dBm) I did not manage to find any dropped packets through multiple people and a wall at a distance of ~20m to ~25m. I do not know why these features were interrupting my data transmissions still, but it's such a breath of fresh air to see any comms at this point.

    Oddities I noticed this time:

    • There were sometimes problems with how I had the modules powered

    The lines I had coming from the power supply were partly at fault for at least some of the time. Earlier today I noticed the receiver module in Rx mode drew significantly less current than normal: ~9mA as opposed to the normal ~20mA. I've moved around the modules a fair bit including connecting/ disconnecting them from the power supply I've been using and when I directly measured the voltage on the modules with longer wires I noticed they were only receiving ~2.2V instead of either 2.8V or 3.3V. Shorter, thicker wires fixed that and now I'm certain I won't be facing any power supply problems again. Even with those new wires, at the time, I was still failing to get communication so I know that was not the only problem I've encountered.

    • The interrupt was sometimes triggering but without my MCU detecting it

    This was because I was too cautious when considering how to connect the IRQ pin of the module to the NRF52 DK. After I noticed the pin being pulled high to 3.3V when the module was powered at that level and the Rx data received interrupt bit being set I connected the MCU to the IRQ pin through a resistor in an attempt to prevent damage to the board. I picked a nearby resistor of what seemed at the time to be a reasonably high value; I ended up connecting 12KR resistors in line for both modules. At some point after connecting the resistors I re-examined the IRQ pins with an oscilloscope to check if they were even triggering since my code was unresponsive and surprisingly they did. The resistor was too high a value so the IRQ pin was dropping near GND potential but the internal pullup and new resistor made a voltage divider for the MCU pin which ended up only dropping to about 1.2V to 1.3V. I'm guessing the pullup resistors inside the NRF52832 are close to/ slightly above 12KR. I took more consideration for what current would be sinking into the pin when connected to 3.3V (3.3V-2.8V = 0.5V difference, 0.5V / 330R = ~1.5mA, should be safe I think). I changed the resistor to a lower value and I haven't had problems with the interrupts triggering but the MCU not detecting them since. This also was not the main culprit behind the failed communications, but is now fixed and no longer a concern.

    • Strange printouts from my debug printf statements

    Even when not attempting to read data over communication, just when debugging my code and reading the register values I've been writing or reading over SPI I noticed that my printout behaved abnormally at times. Notably when I went to use the "print_l01_regs();" function from the main loop of the library you gave me printf was missing large parts of data. I found this just to be some timing issue since including various delays of ~10ms around the print statements generally fixes them so they produce no more errors. When testing transmissions, however, I noticed that I could send data a lot faster with the modules than printf could hope to keep up with. I've been reducing the amount of printf statements and the amount of characters they print gradually while reducing the delay between sent messages on the transmitter side and it's mostly been keeping track. Once I have a delay only in microseconds though my receiver has begun to again not print out correct details with printf statements. I haven't fully debugged if this is the receiver not receiving the packets or if printf still just can't keep up, I'm working on that next.

    • With the address width set to 3B I was receiving false positive packets

    I've tested writing and controlling the length and address data of the modules and I am able to completely control them fine now. I figured for faster data transmission that sending fewer bytes per packet would be ideal so I started testing the 3B address width when transmitting without CRC and ACKs but started getting strange results. My transmitter test right now sends a 31B string and a 1B unsigned number that increments between transmissions, essentially just numbering each packet it transmits. During my tests my receiver was receiving numbers way out of order with seemingly no rhyme or reason behind them, even when my transmitter was turned off. No one else in the office is testing with these or any other modules in the frequency range and I highly doubt that any of the neighboring offices are doing similar tests as me right now. I changed the print out and increased the transmitter delay to read out the entire payload being sent and confirmed that whenever I received a random number I was also receiving a string of garbage values. I do not know where this interference is coming from, and I have only encountered this when working with the modules while the address width is 3B. At 4B or 5B address widths I never encounter these false positives. I have changed the addresses away from the defaults (0xE7E7E7E7E7) to my own values ("ETS", "ETS ", and "ETS  ") and have done my test on RF channel 120 (2.520GHz) as well as the default channel 2 (2.402GHz). I should be fine sending 4B addresses each packet instead of 3B, just a concerning issue I encountered during testing.

    • My modules hate CRC and ACKs

    Regardless of how I have set up both sides so far I have not managed to get them to transmit data at all when using 1B CRC, 2B CRC, or any auto ACKs enabled. These are the main features I believe the datasheet mentions are used in Enhance ShockburstTm, so I'm wondering if maybe I'm currently unable to make use of that to transmit data between modules? Could that maybe be because of the module I have or am I probably still setting something up wrong?

  • Hi Andy

    Thanks for your detailed report. 

    If the UART is running at 115200 baud it could definitely be slower than the radio, which can easily send 500kbps or more. Especially if you add some additional data to each printf statement. 
    You could try to toggle a pin on each received packet instead, or printing just the first byte, to at least see if you are getting the packets you expect without overloading the UART. 

    The false packets you see when using a short address and no CRC is to be expected. When the receiver is enabled you will always be receiving something, whether it is random noise from the receiver chain or a valid packet. Since you are decoding 1 or 2 million samples per second it doesn't take that long for the random noise to match your address, when the chance of accidentally hitting a 3 byte address is about 1 over 16 million. 

    With CRC enabled you would discard most of these packets because of CRC failure, but without it they are all accepted. 

    It will happen with a 4 or 5 byte address as well, but a lot less frequently. 

    When CRC/ACK's make the reception stop it usually means there is some configuration inconsistency between the TX and RX. 

    The channel, bitrate and address is clearly good since you can receive without CRC, but there could be some problem with the payload length configuration. If you don't enable dynamic payload length it is important to configure the payload length in the receiver equal to the length of the payload uploaded on the TX side. 
    Can you double check this?

    Also, can you let me know where you got the module, and what is printed on the nRF24L01+ device?

    There are a lot of counterfeit nRF24L01+ devices out there, and they are not guaranteed to work in the same way. 

    Best regards
    Torbjørn

  • Hi Ovrebekk,

    Sorry for taking so long to make a response, things have not been going very well with these modules and the project is falling behind because of my inability to get these working. 

    void NRF24L_init(void)
    {
        NRF24L.spi_instance = spi;
        NRF24L.pin_ce = CE_PIN;
        NRF24L.pin_irq = IRQ_PIN;
        NRF24L.pin_csn = CSN_PIN;
    
        hal_nrf_nrf52_init(&NRF24L, &NRF24L_config, l01_irq);
    
        nrf_delay_us(5000);
        hal_nrf_set_power_mode(HAL_NRF_PWR_DOWN);
        nrf_delay_us(5000);
        hal_nrf_set_power_mode(HAL_NRF_PWR_UP);
        nrf_delay_us(5000);
    
        hal_nrf_set_rf_channel(RF_CHANNEL); //RF_CHANNEL = 120, default = 2
        hal_nrf_clear_irq_flags_get_status();
        hal_nrf_flush_rx();
        hal_nrf_flush_tx();
        
        nrf_delay_ms(10);
    
        hal_nrf_set_address(HAL_NRF_PIPE0, rf_address);//rf_address[] = "TS E"
        hal_nrf_set_address(HAL_NRF_TX, rf_address);
        hal_nrf_set_address_width(HAL_NRF_AW_5BYTES); //default is 5
    
        hal_nrf_write_reg(EN_AA, 0x00); //auto enables off
        hal_nrf_set_crc_mode(HAL_NRF_CRC_OFF);  //no crc
        nrf_delay_ms(10);
    
        hal_nrf_set_rx_payload_width(PIPE_LINE, PAYLOAD_WIDTH); //Sets payload width
        hal_nrf_set_auto_retr(0, 0);  //disable auto retransmits
    
        #ifdef TRANSMITTER
            hal_nrf_set_irq_mode(HAL_NRF_MAX_RT, false);  //no interrupt for max retransmits
            hal_nrf_set_irq_mode(HAL_NRF_TX_DS, true);    //interrupt for data transmitted
            hal_nrf_set_irq_mode(HAL_NRF_RX_DR, false);   //no interrupt for data received
            hal_nrf_set_operation_mode(HAL_NRF_PTX);  //Set as transmitter
        #endif
        
        #ifdef RECEIVER
            hal_nrf_set_irq_mode(HAL_NRF_MAX_RT, false);  //no interrupt for max retransmits
            hal_nrf_set_irq_mode(HAL_NRF_TX_DS, false);   //no interrupt for data transmitted
            hal_nrf_set_irq_mode(HAL_NRF_RX_DR, true);    //interrupt for data received
            hal_nrf_set_operation_mode(HAL_NRF_PRX);  //Set as receiver
            hal_nrf_chip_enable(CE_ENABLE); //Enter Rx mode
        #endif
        nrf_delay_ms(10);
    }

    Both ends have had the same payload width of 32B, the max allowable size for packets with the NRF24L01+. I've had packets send (without auto acknowledge and CRC) that have been the correct size and data, though sometimes they go missing because there's no acknowledges and sometimes a few bits are flipped because there's no CRC. I have reached the point in this project where not having ACKs or CRC is no longer acceptable and am working towards getting both enabled and functioning correctly.

    Another Devzone thread sounds very alarming to me however because the identification on the ICs of every NRF24L01+ module we have match the code user "orbitcoms" posted here: https://devzone.nordicsemi.com/f/nordic-q-a/36537/nrf24l01-tx-and-rx-address-issue . Our devices' markings read: 

              NRF M

              24L01+

              1808 AH

    We purchased our modules from MakerFocus on Amazon at this link: https://www.amazon.com/MakerFocus-nRF24L01P-Wireless-Transmission-Interface/dp/B07GRMJJY5 

  • Hi Andy

    In that case you should try to find some alternative modules to test as soon as possible, to figure out if the issue is with the module or the code. 

    Ideally something like this one from Sparkfun, which is known to use authentic Nordic devices. 

    If we verify that it is the module that is the issue you can obviously work to find cheaper options that will still work properly. 

    Best regards
    Torbjørn

  • The modules are DEFINITELY functional!

    It took too long for me to get these working so instead we switched to using ESB with the NRF52832s themselves for our communication. With the ESB examples and some effort we made a system that was transmitting decent quality audio over the air in real time successfully. Doing so involved delving into ESB more and getting a better understanding of how everything worked and how the details I overlooked previously. In my original attempts I misunderstood how the pipelines worked and tried manually using pipe 0 incorrectly. I needed to use two pipes to get it working correctly since pipe 0 is used behind the scenes with auto acknowledgements by ESB.
    My current testing setup is using an NRF52832 connected through SPI to one of the NRF24L01+ modules as a receiver catching data sent by an NRF52832 runnning a modified "esb_ptx" example from the proprietary RF examples in the SDK. In the ESB initialization for the transmitter there are configuration settings for both ESB and ESB legacy, the latter of which is intended for use with NRF24L01s. I ensured the settings were the same on both ends to make this work correctly:

    • pipe 0 addresses matching (tested default and custom values)
    • pipe 1 addresses matching (tested default and custom values)
    • tx pipe addresses matching (tested default and custom values)
    • payload lengths matching (32B, no dynamic payload length)
    • RF channels matching (tested default channel and a few others)
    • address widths matching (5B)
    • data rates matching (2Mbps)
    • Auto ACKs enabled
    • CRC lengths matching (2B)

    Thankfully large chunks of the code I had previously been testing with when auto ACKs and CRC were disabled worked correctly with this setup, and I was able to transmit meaningful data consistently. I have only done testing for a short period on this setup so I will keep working on this to make sure that everything is working correctly with these modules as well as replace the transmitting side with another module controlled through SPI. I'm genuinely upset I did not catch this detail earlier since it was such a simple fix to get things working properly but I only understood why after delving deeper into ESB separately.

    I'll keep working with my setup and make an update if I find anything else bizarre since I have somewhere to really start from now!

Reply
  • The modules are DEFINITELY functional!

    It took too long for me to get these working so instead we switched to using ESB with the NRF52832s themselves for our communication. With the ESB examples and some effort we made a system that was transmitting decent quality audio over the air in real time successfully. Doing so involved delving into ESB more and getting a better understanding of how everything worked and how the details I overlooked previously. In my original attempts I misunderstood how the pipelines worked and tried manually using pipe 0 incorrectly. I needed to use two pipes to get it working correctly since pipe 0 is used behind the scenes with auto acknowledgements by ESB.
    My current testing setup is using an NRF52832 connected through SPI to one of the NRF24L01+ modules as a receiver catching data sent by an NRF52832 runnning a modified "esb_ptx" example from the proprietary RF examples in the SDK. In the ESB initialization for the transmitter there are configuration settings for both ESB and ESB legacy, the latter of which is intended for use with NRF24L01s. I ensured the settings were the same on both ends to make this work correctly:

    • pipe 0 addresses matching (tested default and custom values)
    • pipe 1 addresses matching (tested default and custom values)
    • tx pipe addresses matching (tested default and custom values)
    • payload lengths matching (32B, no dynamic payload length)
    • RF channels matching (tested default channel and a few others)
    • address widths matching (5B)
    • data rates matching (2Mbps)
    • Auto ACKs enabled
    • CRC lengths matching (2B)

    Thankfully large chunks of the code I had previously been testing with when auto ACKs and CRC were disabled worked correctly with this setup, and I was able to transmit meaningful data consistently. I have only done testing for a short period on this setup so I will keep working on this to make sure that everything is working correctly with these modules as well as replace the transmitting side with another module controlled through SPI. I'm genuinely upset I did not catch this detail earlier since it was such a simple fix to get things working properly but I only understood why after delving deeper into ESB separately.

    I'll keep working with my setup and make an update if I find anything else bizarre since I have somewhere to really start from now!

Children
Related