Optimizing your nRF24 range with a simple test rig
I'm working on a commercial project and I need a low power wireless interface to send data reliably in a crowded indoor environment. There are many standards and protocols to choose from, but the Nordic Semiconductor nRF24 and its 'shockburst' small packet protocol seemed like the best fit for the job. Three advantages of the nRF24 are its incredibly simple interface, that it uses very little power when idle and that it operates in the unlicensed spectrum near 2.4Ghz. Bluetooth low energy (BLE) could potentially do the same job, but the available devices and RF protocol are a bit more complicated and more expensive than this project requires. I started by buying some inexpensive nRF24L01+ development boards from Amazon that cost around $1 each.
I wired them up to some ATmega328's for testing and tried the simple "Hello World" chat app; they worked as expected. Next, I added them to my product prototype and that's where the trouble began. My project has an ILI9341 320x240 color LCD display sharing the SPI bus with the nRF24 and when the display is active, it interfered with the reception of data on the nRF24. This was the inspiration for my quest to find the right module(s) and optimize the range. I have limited equipment and know very little about RF, but soon discovered that the following variables were affecting the range of the nRF24:
Some of the variables I isolated and tested individually, but then I realized that a test rig which allowed for easy manipulation of multiple variables in real time would be more useful. I decided to design a simple rig using tinned protoboard with socketed parts so that I could re-use the parts and easily swap out different nRF24 modules. Below is a photo of one of the two assembled rigs, and below it, the individual parts (Arduino Pro Mini clone, 64x32 SSD1306 OLED display, Wemos CH340G USB to serial adapter, cheap nRF24 module)
Details: The nRF24 module pictured above has a dipole antenna modification I found here. The ATmega328 has a gap in the nearer female header so that it can't accidentally be connected the wrong way (The missing pin, A1, won't be missed).
Parts used:
Tinned protoboard, buttons, female + male headers, silicone coated 30AWG wire < $1.00
nRF24 module $1.20
64x32 I2C OLED $4.85 (larger ones are cheaper and can be substituted)
CH340G USB-serial adapter $0.80
Arduino Pro Mini clone (8Mhz, 3.3v) $2.12
Total cost ~ $10
The test rig code is available here: Github repo
After having some issues and reading more articles, I decided to buy a few more nRF24 variations for testing
There are plenty of individual articles about working with the nRF24. I wrote this blog entry to collect a lot of the useful information together in one place. Here are some notes which describe what I learned about each of the above variables:
Power Supply Noise and/or dropouts
This issue first appeared when I activated the LCD display. The nRF24 refused to receive anything when the LCD was active. The LCD could be powered on (but not initialized), and all was well, but once initialized, the nRF24 refused to receive data. I found various articles mentioning how sensitive the nRF24 is to power supply noise. Without an oscilloscope, I had no way to know the fundamental frequency of the noise, so I just took the advice of others and added a 0.1uF ceramic capacitor across the power leads (small brown disc in the photos above). This allowed it to work, but could it work better with a more precise noise filter? This same 'fix' worked for all of the various modules in the photo above. The other issue to consider from your power supply is that it can handle the current swings required to drive the transmitter without having the voltage sag. For the smaller board, 7-11mA shouldn't be a big issue for even a tiny power supply, but the 100-150mA swings of the more powerful boards can be enough to make the voltage sag and interfere with the operation of the nRF24.
Local Data Noise
Besides the sensitivity to the power supply noise, local activity on the nRF24 (SPI data) also interferes with data reception. Polling the "available()" function will cause noise on the SPI lines which can interfere with reception. The IRQ line is there to make life easy. When data is available, the IRQ line goes low and stays low until the data is read. An nRF24 set up as a receiver should be left "quiet" to not interfere with reception.
External Interference/Noise
Part of the problem with using the 2.4Ghz frequency range is that a lot of other devices are operating in that range. I'm not sure about microwave ovens and non-radio sources of RF noise, but wifi, bluetooth and zigbee operate in the same frequency range and can interfere with your nRF24 packets.
Data Rate
The nRF24 supports data rates of 250Kbs, 1Mbs, and 2Mbs. Due to the way the data is transmitted, a slower data rate provides higher resistance to noise and allows a longer range. For my purposes, 250Kbs is plenty fast.
Packet Length
The nRF24 allows transmitting a payload of up to 32 bytes at a time (default size). By default, it adds a 16-bit CRC and (64-bit?) destination address. It includes an auto-retry mechanism that waits for an acknowledgement after each packet is sent. The longer the packet, the higher the chance that corruption will occur from another RF source in the middle of your transmission. I verified that 8-byte payloads have a much higher success rate than 32-byte payloads at a given distance. For my purposes, 8-bytes are enough and this allows me to send them further with fewer retries (lower total power).
Antenna Type and Orientation
The type, size and orientation of the antenna are critical to how well the data gets from point A to point B. The little PCB antennas are the most convenient and the least efficient. I'm no expert on Antenna design, but the 1/4 wavelength dipole (either pre-built or homemade) perform better than the little squiggle PCB antenna provided on most dev boards. The PCB antenna is highly directional, and not symmetric. The dipole antenna can radiate out or receive energy in all directions equally while the PCB antenna is much more sensitive to orientation. This is easy to see experimentally when running my test rig. At certain angles, the PCB antenna will receive 100% of the packets and rotate it slightly and it will receive 0%. Part of its success is probably due to reflections of the transmissions hitting the antenna from different directions.
Counterfeit Versus Genuine Parts
As is the case with most popular electronic parts, the nRF24 has been copied by Chinese manufacturers. From reading about other's experiences, it appears that the vast majority on the market are clones. The performance (energy usage and receiver sensitivity) are supposedly worse than the originals. I can't really comment on this because it appears that all of my boards are counterfeit. A feature of the genuine nRF24L01+ is that it has a bit indicating the received signal strength (1 means > -64dB). This doesn't seem to work on any of my boards that claim they are nRF24L01+ SoCs. If you source your parts with care and pay a little extra, genuine parts can be found. Also, some vendors sell the non-plus part marked as "plus". A feature I need from the + part is support for the 250Kbs data rate. The slower data rate increases reliability of transmission.
Channel Frequency
The nRF24 allows you to select one of 126 possible channels (0-125). All are 1Mhz wide and start at 2400Mhz (2400-2525Mhz). Wifi channels and bluetooth also occupy this same range, but wifi tends to choose lower channel numbers. This is one of the data points that was easy to test with my rig - channels 76 (default) and above tend to get the least interference from other data devices. Below is an image of the wifi channels and the frequencies they cover:
The Test Rig
My intention in creating the test rig was to be able to leave the receiving unit in a certain spot and walk around with the transmitting unit and vary the channel, transmit power and antenna orientation to see how well it performs. I decided that 2 buttons and a small display were enough to accomplish that task. One button steps through the transmit power (0,1,2,3 and then back to 0). The second button increments the channel number and wraps around to 0 when it reaches the end (125). Changing the channel on the transmitter sends a packet to the receiver which tells it to switch to that channel as well, so that one person can perform all tests independently from the transmitter side. The OLED display can be switched out for a 7-segment LED or a different sized OLED, but I felt that the 64x32 OLED was perfect for this task. For some reason, the 64x32 version of that (SSD1306 OLED) is more expensive than all other sizes. Perhaps because it's less popular than the 0.96" 128x64 version.
Here is the Fritzing diagram which shows the connections I used. I chose to use my Bit-bang I2C library so that I wouldn't have to solder the oddly positioned A4/A5 pins on the Arduino Pro Mini board.
I've had to revisit this topic recently for my commercial products and have discovered a new source of trouble. The auto-ACK feature of the nRF24 is very convenient for letting you know that your write() succeeded. The problem I'm finding (again with the pre-made modules) is that even 2 modules from the same vendor sometimes don't work reliably. The write() will succeed, but auto-ack returns a failure. Obviously this can be reception problems as mentioned earlier in this blog post, but it seems to go beyond that. I've got several identical modules from the same vendor and when put in the same conditions, with the same distance/orientation, some refuse to auto-ACK and others work fine. I've researched this on the web and found some odd answers about address choices and library bugs. I'm convinced this has to do with variations in knockoff chips. I've written my own brute-force ACK mechanism and it appears to work on the boards that fail to auto-ACK. If I get any additional info on this, I'll post it here.
I wired them up to some ATmega328's for testing and tried the simple "Hello World" chat app; they worked as expected. Next, I added them to my product prototype and that's where the trouble began. My project has an ILI9341 320x240 color LCD display sharing the SPI bus with the nRF24 and when the display is active, it interfered with the reception of data on the nRF24. This was the inspiration for my quest to find the right module(s) and optimize the range. I have limited equipment and know very little about RF, but soon discovered that the following variables were affecting the range of the nRF24:
- Power supply noise and/or dropouts
- Local data noise
- External interference/noise
- Data rate
- Packet length
- Antenna type and orientation
- Counterfeit versus genuine parts
- Channel frequency
- Transmit power
Some of the variables I isolated and tested individually, but then I realized that a test rig which allowed for easy manipulation of multiple variables in real time would be more useful. I decided to design a simple rig using tinned protoboard with socketed parts so that I could re-use the parts and easily swap out different nRF24 modules. Below is a photo of one of the two assembled rigs, and below it, the individual parts (Arduino Pro Mini clone, 64x32 SSD1306 OLED display, Wemos CH340G USB to serial adapter, cheap nRF24 module)
Details: The nRF24 module pictured above has a dipole antenna modification I found here. The ATmega328 has a gap in the nearer female header so that it can't accidentally be connected the wrong way (The missing pin, A1, won't be missed).
Parts used:
Tinned protoboard, buttons, female + male headers, silicone coated 30AWG wire < $1.00
nRF24 module $1.20
64x32 I2C OLED $4.85 (larger ones are cheaper and can be substituted)
CH340G USB-serial adapter $0.80
Arduino Pro Mini clone (8Mhz, 3.3v) $2.12
Total cost ~ $10
The test rig code is available here: Github repo
After having some issues and reading more articles, I decided to buy a few more nRF24 variations for testing
There are plenty of individual articles about working with the nRF24. I wrote this blog entry to collect a lot of the useful information together in one place. Here are some notes which describe what I learned about each of the above variables:
Power Supply Noise and/or dropouts
This issue first appeared when I activated the LCD display. The nRF24 refused to receive anything when the LCD was active. The LCD could be powered on (but not initialized), and all was well, but once initialized, the nRF24 refused to receive data. I found various articles mentioning how sensitive the nRF24 is to power supply noise. Without an oscilloscope, I had no way to know the fundamental frequency of the noise, so I just took the advice of others and added a 0.1uF ceramic capacitor across the power leads (small brown disc in the photos above). This allowed it to work, but could it work better with a more precise noise filter? This same 'fix' worked for all of the various modules in the photo above. The other issue to consider from your power supply is that it can handle the current swings required to drive the transmitter without having the voltage sag. For the smaller board, 7-11mA shouldn't be a big issue for even a tiny power supply, but the 100-150mA swings of the more powerful boards can be enough to make the voltage sag and interfere with the operation of the nRF24.
Local Data Noise
Besides the sensitivity to the power supply noise, local activity on the nRF24 (SPI data) also interferes with data reception. Polling the "available()" function will cause noise on the SPI lines which can interfere with reception. The IRQ line is there to make life easy. When data is available, the IRQ line goes low and stays low until the data is read. An nRF24 set up as a receiver should be left "quiet" to not interfere with reception.
External Interference/Noise
Part of the problem with using the 2.4Ghz frequency range is that a lot of other devices are operating in that range. I'm not sure about microwave ovens and non-radio sources of RF noise, but wifi, bluetooth and zigbee operate in the same frequency range and can interfere with your nRF24 packets.
Data Rate
The nRF24 supports data rates of 250Kbs, 1Mbs, and 2Mbs. Due to the way the data is transmitted, a slower data rate provides higher resistance to noise and allows a longer range. For my purposes, 250Kbs is plenty fast.
Packet Length
The nRF24 allows transmitting a payload of up to 32 bytes at a time (default size). By default, it adds a 16-bit CRC and (64-bit?) destination address. It includes an auto-retry mechanism that waits for an acknowledgement after each packet is sent. The longer the packet, the higher the chance that corruption will occur from another RF source in the middle of your transmission. I verified that 8-byte payloads have a much higher success rate than 32-byte payloads at a given distance. For my purposes, 8-bytes are enough and this allows me to send them further with fewer retries (lower total power).
Antenna Type and Orientation
The type, size and orientation of the antenna are critical to how well the data gets from point A to point B. The little PCB antennas are the most convenient and the least efficient. I'm no expert on Antenna design, but the 1/4 wavelength dipole (either pre-built or homemade) perform better than the little squiggle PCB antenna provided on most dev boards. The PCB antenna is highly directional, and not symmetric. The dipole antenna can radiate out or receive energy in all directions equally while the PCB antenna is much more sensitive to orientation. This is easy to see experimentally when running my test rig. At certain angles, the PCB antenna will receive 100% of the packets and rotate it slightly and it will receive 0%. Part of its success is probably due to reflections of the transmissions hitting the antenna from different directions.
Counterfeit Versus Genuine Parts
As is the case with most popular electronic parts, the nRF24 has been copied by Chinese manufacturers. From reading about other's experiences, it appears that the vast majority on the market are clones. The performance (energy usage and receiver sensitivity) are supposedly worse than the originals. I can't really comment on this because it appears that all of my boards are counterfeit. A feature of the genuine nRF24L01+ is that it has a bit indicating the received signal strength (1 means > -64dB). This doesn't seem to work on any of my boards that claim they are nRF24L01+ SoCs. If you source your parts with care and pay a little extra, genuine parts can be found. Also, some vendors sell the non-plus part marked as "plus". A feature I need from the + part is support for the 250Kbs data rate. The slower data rate increases reliability of transmission.
Channel Frequency
The nRF24 allows you to select one of 126 possible channels (0-125). All are 1Mhz wide and start at 2400Mhz (2400-2525Mhz). Wifi channels and bluetooth also occupy this same range, but wifi tends to choose lower channel numbers. This is one of the data points that was easy to test with my rig - channels 76 (default) and above tend to get the least interference from other data devices. Below is an image of the wifi channels and the frequencies they cover:
Transmit Power
The small PCB nRF24 modules can transmit a maximum of about 10mW of radio energy, using 11.3mA of current (@3.3v) to do it. There are 4 user-settable transmission power levels. The higher powered modules (left 2 shown in the photo above) can transmit up to 100mW of radio energy and use 100-150mA of current. The higher powered boards typically also include a low noise amplifier (LNA) to improve radio reception. Shielding the electronics (left most board) also improves radio reception by blocking the RF noise coming from the SoC. The leftmost board claims it can transmit just over 2Km with a clear line of sight (no obstacles/walls). I've seen claims of much longer distances by using more advanced directional antennas as well.The Test Rig
My intention in creating the test rig was to be able to leave the receiving unit in a certain spot and walk around with the transmitting unit and vary the channel, transmit power and antenna orientation to see how well it performs. I decided that 2 buttons and a small display were enough to accomplish that task. One button steps through the transmit power (0,1,2,3 and then back to 0). The second button increments the channel number and wraps around to 0 when it reaches the end (125). Changing the channel on the transmitter sends a packet to the receiver which tells it to switch to that channel as well, so that one person can perform all tests independently from the transmitter side. The OLED display can be switched out for a 7-segment LED or a different sized OLED, but I felt that the 64x32 OLED was perfect for this task. For some reason, the 64x32 version of that (SSD1306 OLED) is more expensive than all other sizes. Perhaps because it's less popular than the 0.96" 128x64 version.
Here is the Fritzing diagram which shows the connections I used. I chose to use my Bit-bang I2C library so that I wouldn't have to solder the oddly positioned A4/A5 pins on the Arduino Pro Mini board.
The Software
When the sketch is started, the display indicates if the nRF24 module was successfully initialized or not and then waits 2 seconds. During those 2 seconds, if you hold down either one of the pushbuttons, the software will go into RX mode, otherwise it will be in TX mode. In either mode, pressing both buttons simultaneously will reset the software and allow you to switch modes again.
Once in TX mode, you can press button 0 to step through the 4 available power levels (0-3) or button 1 to step through the available channels (0-125). Changing the channel will also send a "channel change" packet to the receiver so that it can follow you without user intervention. The TX side will send packets 2 times per second and displays the ACK status on the OLED. If no ACK is received, the packet didn't reach the receiver or the receiver wasn't able to get the ACK back to the transmitter. For true nRF24L01+ modules, it will show the receive strength as "strong" or "weak". The RX side status will show the current channel number and the total number of packets received. It's easy to see if the data is not flowing because the counter will stop incrementing.
My Results
My goal is to have a low powered transmitting unit which runs for many months from a coin-cell battery and a receiver which can run from mains power. This asymmetric arrangement works well for the nRF24 since I can have a high powered unit with LNA on the receiver side and the PCB version on the transmitting side. For my project, I tested indoors in an apartment with many neighbors' Wifi and plenty of walls and electronics interfering with the signal. Here's what I found:
- Low and high numbered channels performed the best; middle channels performed the worst.
- PCB antenna to PCB antenna - This arrangement gave the worst results and was highly directional. Rotating the board would cause the signal to work and then not work. At the lowest power setting, I could reliably go about 10 meters through one wall. At the highest power setting, it only would go about 15 meters and 2 walls.
- The dipole mod to the PCB antenna reduced the sensitivity to orientation, but didn't seem to affect the range versus a well oriented PCB antenna. This certainly improves the reliability, but is too large+fragile for my needs.
- PCB antenna (TX) to dipole (w/LNA) (RX) would go 20-30 meters through various obstacles. With the transmitter set at the lowest power, it could go the full distance if oriented correctly. At higher power levels, the orientation of the antenna was less critical. This is the arrangement I'll be using for my project.
- I tried one outdoor experiment with the PCB antenna on the TX side and the high powered + LNA on the RX side. This was still highly sensitive to orientation, but was able to go about 70-80 meters before it stopped working.
I've had to revisit this topic recently for my commercial products and have discovered a new source of trouble. The auto-ACK feature of the nRF24 is very convenient for letting you know that your write() succeeded. The problem I'm finding (again with the pre-made modules) is that even 2 modules from the same vendor sometimes don't work reliably. The write() will succeed, but auto-ack returns a failure. Obviously this can be reception problems as mentioned earlier in this blog post, but it seems to go beyond that. I've got several identical modules from the same vendor and when put in the same conditions, with the same distance/orientation, some refuse to auto-ACK and others work fine. I've researched this on the web and found some odd answers about address choices and library bugs. I'm convinced this has to do with variations in knockoff chips. I've written my own brute-force ACK mechanism and it appears to work on the boards that fail to auto-ACK. If I get any additional info on this, I'll post it here.
Excellent post!!! I have been playing with nrf24l01+ also in the external antena version (second from the left in you photo) and also have had problems with the auto-ack (specifically with auto-ack with payload feature). Did you find any source cause? In our experiments, the antenna seems to generate the trouble specially in channels 50-70)
ReplyDelete