[Request - Answered] nRF24L01+ library

Hi, I want to use my Spark Core to bridge my cheap nRF24 network to the Internet, which means I need to setup the nRF24L01+ chip within the Spark Core.

I’m using one of those 8 pins SPI based boards and I’ve connected it to the Spark Core SPI plus digital pins 5 and 6.

I’ve then picked the nRF24L01 library from GitHub (maniacbug implementation) and modified it to compile within the Spark Build environment, but my Arduino is unable to see the packets getting ponged back from the Spark (obviously the same library and code without modifications is working nicely between two Arduinos.

The code I’m flashing onto the Spark is pretty simple and available here.

Because I’m new to Cortex programming I’m seeking for help both on the wiring (may be I’ve missed something or picked the wrong pins for CE/CSN) and the code.

I’m quite sure I will not be the only one willing to interface this great device to those cheap networking chips.


The great @BDub has succesfully ported the RF24 library to :spark:, sources are available on Github.

Special thanks go to @wgbartley who provided sample boards.

Thanks folks, you rock!

1 Like

Just a quick scan through your port:

pgm_read_byte(x) (*(x)); is probably not going to work on the Spark Core like it does on the AVRs. You might try converting the tables that it’s looking up into int arrays.

If this PSTR() is a way to put strings into flash instead of RAM, you are going to have a little bit of work to do :wink:
printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n")

This will need to be adjusted with one of the :spark: Core dividers…
SPI.setClockDivider(SPI_CLOCK_DIV4); // 16MHz / 4 = 4Mhz

On the Spark Core we have a 72Mhz clock so to get the same 4MHz you need to divide by 18. However there is no DIV18 prescaler. So we error on the faster side in this case since the comments warn to stay at least 2x the data rate.
SPI.setClockDivider(SPI_CLOCK_DIV16); // 72MHz / 16 = 4.5Mhz

RF24 radio(5,6); needs to define the CE and CSN pins properly on the Spark Core.

RF24::RF24(uint8_t _cepin, uint8_t _cspin):
uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */
  uint8_t csn_pin; /**< SPI Chip select */

A2 is the default SS (SPI Chip Select) pin… and it looks like you just need to give it another digital output for the CE pin.

So I’d suggest:
RF24 radio(A2,D2);

I’m sure there is a bunch more work to do! And I have no nRF24L01+‘s to help figure it out much more than a quick look at the code. Keep hackin’ at it!

1 Like

I’m waiting on my replacement :spark: to come in and that’s exactly what I plan on using it for, and it looks like you’ve got it started! I have a small army of AT328s and nRF24L01+s (well, about 10 of each) sitting around waiting for a master “gateway” to use for maniacbug’s RF24Network.

At some point, I need to play around with getting the AT328s and nRF24L01+s to work on the same LD1117V33s to make things a little easier (instead of an extra LM7805 to regulate for just the AT328).

I’d be more than happy to send you a couple from my stash to get your expertise on this project. If you’re interested, shoot me a message, and I’ll put them in the mail ASAP.

1 Like

With regards to [quote=“BDub, post:2, topic:2286”]
A2 is the default SS (SPI Chip Select) pin… and it looks like you just need to give it another digital output for the CE pin.

So I’d suggest:RF24 radio(A2,D2);

I understand your suggestion, but I hope there’s no counter indication in using anything else other than A2, mostly because I need analog inputs and the SPI is already using three analog pins. The CSN/SS pin should be user selectable…

I’ll apply your suggestions in a few hours and post any feedback.

You can use any pin you want for the SS… because SS is driven by the library you implement, not the SPI Class. Well, for the most part. When you SPI.begin(); the SS pin A2 is set to an output and set HIGH.

If you ran analogRead(A2); after you SPI.begin();, it will reconfigure that pin as an analog input and you are good. However, if you can’t tolerate your analog circuitry being set high momentarily, it’s not a good idea to reuse A2 as an analog input.

I’m thinking it might be a good idea to rewrite the SPI.begin() to include another constructor for SPI.begin() with an argument for the SS pin. And then .begin as it sits today will become .init() which gets called from .begin(). If anyone agrees with this I’ll put together a pull request.

void SPIClass::begin() {
    init(SS); // Use pin A2
void SPIClass::begin(uint16_t ss_pin) {
    if (ss_pin >= TOTAL_PINS )
    init(ss_pin); // User defined SS pin
void SPIClass::init(uint16_t ss_pin) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    pinMode(MISO, INPUT);
    pinMode(ss_pin, OUTPUT);
    digitalWrite(ss_pin, HIGH);


I believe just redefining PSTR as below should do the trick: we have pleanty of RAM on the :spark:, shouldn’t it work?

#define PSTR(x) (x)

I believe this should be the other way around because the first param is CE and the second CSN (aka SS, but I didn’t know that until you pinpointed it)

RF24 radio(D2, A2)

I made all the changes, rewired accordingly but still no joy.

Sadly I’m still using the cloud build so I need to move into a wired programming mode to further investigate this.

RF24 radio(D2, A2); exactly! :wink:

I’m thinking maybe #define PSTR(x) #x

I have no idea what that might do :cry:, sorry, I’m a Java expert and we have no prepocessor…
Unless it is a typo and you meant (*(x))

Ok, after some playing… it seems we can just remove the PSTR() string wraps… or define this macro at the top:
#define PSTR(x) #x

By default it seems string constants are stored in FLASH… so something like these two examples are put into flash memory:
static const char str[] = "freeMemory()="; Serial.print(str);


Here’s a program to play with that let’s you see the memory effects of doing different things:

#define PSTR(x) #x

unsigned int __heap_start;
void *__brkval;

 * The free list structure as maintained by the 
 * avr-libc memory allocation routines.
struct __freelist {
  size_t sz;
  struct __freelist *nx;

/* The head of the free list structure */
struct __freelist *__flp;

// MemoryFree library based on code posted here:
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15
// Extended by Matthew Murdoch to include walking of the free list.
int freeMemory();

/* Calculates the size of the free list */
int freeListSize() {
  struct __freelist* current;
  int total = 0;

  for (current = __flp; current; current = current->nx) {
    total += 2; /* Add two bytes for the memory block's header  */
    total += (int) current->sz;

  return total;

int freeMemory() {
  int free_memory;

  if ((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__heap_start);
  } else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
    free_memory += freeListSize();
  return free_memory;
// Use only one at a time   
//static const char str[] = "freeMemory()="; // 17952
char str[] = "freeMemory()="; // 17936 (16 bytes put into RAM)

void setup() {

void loop() {
    // Use only one at a time        
    Serial.print(str); // 17952 (static const char str[] = "freeMemory()=";)
    //Serial.print("freeMemory()="); // 17952
    //Serial.print(PSTR("freeMemory()=")); // 17952

Now that the weekend is finally here, I’ll be able to join in on the fun. I’m going to get a couple of Arduinos talking first, just so I know I have a reliable network to test with. Then I’ll join in on porting the Spark over!

I also have a 3D printer in mid-assembly that was missing some hardware. I just picked up the hardware this evening, so forgive me if I get a little distracted!

I’ve updated the Gist with latest suggestions and added some debug statements which I’m now able to see because I’ve set up the USB serial to the :spark:.

My test setup is composed by an Anrduino Uno with a nRF24L01+ running this code while the :spark: is connected to another nRF24L01+ running this code.

So far no joy in seeing the small packet getting through.

UPDATE what I noticed is that if I invoke the printDetails function I get plenty of errors I’m unable to fix. Because that function gets access to the Nordic chip I believe that might lead us to the root of the problem.

I’ve forked the RF24 project on github and started applying the fixes we have found so far: I’ll be very happy to get you guys onboard if you want to collaborate on one single repo.

I’ve got some nRF24L01’s in hand, thanks @wgbartley! Will do my best to start working on this this weekend. I have about 3 other projects to make progress on, and plenty of snow shoveling to do as well. One of my projects could use these inexpensive RF modules though so I’m definitely motivated to DO IT ALL!!! mwuahahaha.

@wgbartley @rlogiacco Hey are you guys trying to convert this one:

or this one:

I guess I’m not sure what the difference is

@BDub the former is the driver library to control the chip, the latter builds on top of the first one to provide some network abstraction layer.

I believe that once we have one we should be able to get the other quite easily.

My goal was a little broader though: create one single library (if possible) covering the nRF24 on Raspberry, Arduino and Spark, that’s why I cloned https://github.com/yovio/RF24 which includes the RPi driver.

We might want to set a smaller goal and proceed by increments: let’s start with porting the https://github.com/maniacbug/RF24 to our :spark: .

I haven’t made any progress on this due to some errors I’m getting within my Eclipse environment…

Ok I see now, the examples in the RF24Network include both the driver and the network class:

#include <RF24Network.h>
#include <RF24.h>

Step one for me is to hook these two drivers up to some arduinos and get this example working:

Then port that to the Spark Core + Arduino.

Then hopefully it should work between two Spark Core’s.

I’ll start from scratch so as not to introduce any errors/assumptions from the start, and then if I get stumped we can compare ports.

In order to save you some time I’ve personally wasted myself, whenever you include the RF24.h add an include call to the printf.h file you can find in the examples/pingpair folder (and in many others): without that inclusion my Arduino Micro was crashing immediately without any sign of life. I discovered that just yesterday when I was trying to setup my own comparison test environment and I wished to start from a clean setup.

My porting path was the same as yours, I believe we can use each other test environment for confirmation: mine is based on two Arduino Micro, two :spark: and 3 nRF24 boards, all mounting the nRF24L01+ chip.

Once you have the board properly connected you can use the RF24.printDetails() function to check the model, other details and the connections.

Here a couple of tricks regarding the RF24 library and the cheap nRF24L01+ boards:

  • wiring check can be done using printDetails function which returns meaningful values (something not equals to 0xFF or 0x00) for the different registers
  • power line decoupling seems to be required even for regulated voltages, a 1-100uF polarized capacitor between Vcc and GND fixes the issue

I’ve a test rig set up now: have you guys had the chance to make any progress?

I didn’t get a chance to wire up my proto shields with a female 2 x 5 socket, but it’s next on my list. Once I have it working on the Arduino, it will be easy to go to the Spark because I have some Shield Shields :smile:

Just to check: did you mean 2 x 4? My boards have 8 pins, 7 actually required.

Yeah! My memory was off from what it is 2 x 4… been swamped at work lately but I keep bringing these with me wherever I go just in case I get some free time at the soldering iron.

1 Like