NRF24L01 Channel scan

I used an NRF24L01+ attached to an Intel Galileo Gen2 to do an RF channel scan in an effort to identify a “good” choice for a channel that will best avoid interference. The program repeatedly changes channel and monitors the Received Power Detect Bit (Register 9). The program ran for and hour and counted up the number of times the RPD bit was 1 for each of the 128 channels and produced an output like this:

0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0
1 0 0 0 1 0 1 1 0 2 1 0 0 0 0 1
0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0
0 0 1 0 0 1 3 6 3 2 4 9 4 7 12 5
4 7 2 3 7 5 1 3 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Each step across the output array represents a 1MHz step in frequency. The start frequency is 2400MHz. The value ’12’ occurs at a frequency of 2462MHz. This corresponds to Wifi Channel 11 – a fact confirmed by doing a Wifi Channel scan on my laptop which showed two Wifi access points operating at this frequency.
It is worth noticing that there is not much happening beyond 2471MHz. This is probably because Wifi stops around here (at least in Europe). So is this the answer – simply stay outside the Wifi bands? In Europe, the Wifi bands range from 2412MHz to 2472MHz so maybe the best NRF24L01 channels are less than 12 and greater than 72. Time will tell….

Code is below (node.js)


// NRF24L01+ demo in javascript for the Intel Galileo Gen 2
// Author: Frank Duignan.  
// Updates posted on http://ioprog.com and http://eleceng.dit.ie/frank

var PAYLOAD_LENGTH=10
var SOURCE_ADDRESS=([8,0,5,8,6]);
var DESTINATION_ADDRESS=([0x0a,1,1,1,4]);
/******* Low level setup (configure I/O etc *******/
var m=require('mraa');
console.log('MRAA version: ' + m.getVersion());
// configure SPI for 1MHz operation
var SPI=new m.Spi(0);
SPI.frequency(1000000);
// Set up CE and CSN pins for the NRF24L01
// These will be directly controlled by software NOT the SPI hardware
var CEPin=new m.Gpio(9);
var CSNPin=new m.Gpio(8);
CEPin.dir(m.DIR_OUT);
CSNPin.dir(m.DIR_OUT);
CEPin.write(0);
CSNPin.write(1);
// Global variable that is updated during each SPI transaction
var NRFStatus;


// NRF Functions follow
function NRFinit()
{		
	NRFWriteRegister(0,0);			// Clear out config
	NRFWriteRegister(1,3);			// enable auto ack in P0 and P1
	NRFWriteRegister(4,0x24);		// 750us between retries, 4 retries
	NRFWriteRegister(2,3);			// enable data pipe 0 and 1
	NRFWriteRegister(3,3);			// 5 byte addressing
	NRFWriteRegister(0x05,5); 		// select channel 5
	NRFWriteRegister(0x06,0x06); 	// select 1Mbps, maximum power
	NRFWriteRegister(0x07,0x70); 	// clear status flags
	NRFWriteRegister(0x11,0);		// Auto ack in pipe 0
	NRFWriteRegister(0x12,PAYLOAD_LENGTH);	// set payload length
	NRFWriteCE(1);
	NRFEnableRXMode();				// start listening
}
function delay(howlong)
{
	howlong = howlong / 2; // yields approx 1us timebase
	while(howlong > 0)
	{
		howlong = howlong -1 ;
	}
}
function NRFWriteCE(Value)
{	
	if (Value)
		CEPin.write(1)
	else
		CEPin.write(0)
		
}
function NRFWriteCSN(Value)
{
	if (Value ) 
		CSNPin.write(1);
	else
		CSNPin.write(0);
}

function transferSPI(data)
{		
	return SPI.writeByte(data);
}

function NRFReadRegister(RegNum)
{
	var ReturnValue=0;
	NRFWriteCSN(0);	
	if (RegNum < 0x20)
	{
		NRFStatus = transferSPI(RegNum); // update status after CSN goes low
		ReturnValue = transferSPI(0xff); // Send dummy byte to generate clocks		
	}
	else
	{
		ReturnValue = -1;
	}
	NRFWriteCSN(1);		
	return ReturnValue;
}

function NRFWriteRegister(RegNum, Value)
{
	var ReturnValue=0;
	NRFWriteCSN(0);	
	if (RegNum < 0x20)
	{		
		NRFStatus = transferSPI(0x20+RegNum); // update status after CSN goes low				
		ReturnValue = transferSPI(Value);	  // Write byte to target
	}
	else
	{
		ReturnValue = -1;
	}
	NRFWriteCSN(1);
	return ReturnValue;
}
function NRFFlushTX()
{
	NRFWriteCSN(0);	
	NRFStatus = transferSPI(0xe1); // Send Flush TX command
	NRFWriteCSN(1);
}
function NRFFlushRX()
{
	NRFWriteCSN(0);	
	NRFStatus = transferSPI(0xe2); // Send Flush RX command
	NRFWriteCSN(1);
}

function NRFSetRXAddress0(AddressLength, Address)
{
	var index;
	switch (AddressLength) {
		case 3 : {
			NRFWriteRegister(3,1); // 3 byte address length			
			break;
		}
		case 4 : {
			NRFWriteRegister(3,2); // 4 byte address length
			break;
		}
		case 5 : {
			NRFWriteRegister(3,3); // 5 byte address length
			break;
		}
		default: {
			return -1; // invalid address length
		}
	}
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x20+0x0a); // start write to RX_P0_Pipe address
	for (index = 0; index < AddressLength; index++)
	{
		NRFStatus = transferSPI(Address[index]);
	}
	NRFWriteCSN(1);
}
function NRFSetRXAddress1(AddressLength, Address)
{
	var index;
	switch (AddressLength) {
		case 3 : {
			NRFWriteRegister(3,1); // 3 byte address length			
			break;
		}
		case 4 : {
			NRFWriteRegister(3,2); // 4 byte address length
			break;
		}
		case 5 : {
			NRFWriteRegister(3,3); // 5 byte address length
			break;
		}
		default: {
			return -1; // invalid address length
		}
	}
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x20+0x0b); // start write to RX_P1_Pipe address
	for (index = 0; index < AddressLength; index++)
	{
		NRFStatus = transferSPI(Address[index]);
	}
	NRFWriteCSN(1);
}
function NRFGetRXAddress( MaxAddressLength,  Address)
{
	var index;
	var actual_length;
	actual_length = NRFReadRegister(3);
	actual_length = actual_length + 2;
	if (actual_length > MaxAddressLength)
		return -1;
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x0a); // start read from RX_P0_Pipe address
	for (index = 0; index < actual_length; index++)
	{
		Address[index] = transferSPI(0xff);
	}
	NRFWriteCSN(1);
	return(0);
}
function NRFSetTXAddress(AddressLength, Address)
{
	var index;
	switch (AddressLength) {
		case 3 : {
			NRFWriteRegister(3,1); // 3 byte address length			
			break;
		}
		case 4 : {
			NRFWriteRegister(3,2); // 4 byte address length
			break;
		}
		case 5 : {
			NRFWriteRegister(3,3); // 5 byte address length
			break;
		}
		default: {
			return -1; // invalid address length
		}
	}
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x20+0x10); // start write to TX address
	for (index = 0; index < AddressLength; index++)
	{
		transferSPI(Address[index]);
	}
	NRFWriteCSN(1);
	return(0);
}

function NRFGetTXAddress(MaxAddressLength, Address)
{
	var index;
	var actual_length;
	actual_length = NRFReadRegister(3);
	actual_length = actual_length + 2;
	if (actual_length > MaxAddressLength)
		return -1;
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x10); // start read from TX address
	for (index = 0; index < actual_length; index++)
	{
		Address[index] = transferSPI(0xff);
	}
	NRFWriteCSN(1);
	return(0);
}
function NRFEnableTXMode()
{
	NRFWriteRegister(0,0x0a); // enable CRC, power up
}
function NRFEnableRXMode()
{
	NRFWriteRegister(0,0x0b); // enable CRC, power up, RX mode
}
function NRFWriteData(Length, Data)
{
	var index;
	if (Length > 32)
		return -1; // too long
	NRFWriteCE(0);
	NRFWriteRegister(0x07,0x70); // clear RX_DR,TX_DS,MAX_RT bits
	NRFEnableTXMode();
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0xa0); // start write to TX buffer
	for (index = 0; index < Length; index++)
	{
		transferSPI(Data[index]);
	}
	NRFWriteCSN(1);
	NRFWriteCE(1);	
	console.log("Sending..");
	NRFEnableRXMode();
}
function NRFReadData(MaxLength,Data)
{ // data is assumeed to be in data pipe 1
	var available_bytes;
	var index;
	var Length;
	available_bytes=NRFReadRegister(0x12); // find out how many bytes are available in P1
	if (available_bytes == 0)
		return 0;
	NRFWriteCSN(0);
	NRFStatus = transferSPI(0x61); // start read from RX buffer
	if (available_bytes > MaxLength)
		Length = MaxLength;
	else
		Length = available_bytes;
	for (index = 0; index < Length; index++)
	{
		Data[index]=transferSPI(0xff);
	}
	NRFWriteCSN(1);
	return Length;
}
function printRegisters()
{
	var regnum;
	for (regnum = 0;regnum < 0x20; regnum++)
	{		
		var hexstring= NRFReadRegister(regnum).toString(16); 
		console.log(regnum.toString(16) + " : " + hexstring);
	}
}
/* The main body of the program follows.  
 * The NRF is initialized with correct addresses etc and is periodically
 * polled to see if there is any data available in the RX FIFO
 * If there is data it is read and a byte is sent back
*/
var ChannelTraffic = new Buffer(128);
function poll()
{
	// check to see if there is any received data
	var cd=NRFReadRegister(0x09);
	//console.log(Channel+":"+cd);
	NRFWriteRegister(0x05,Channel); 		// select channel 
	NRFWriteCE(1);	
	NRFEnableRXMode();
	NRFWriteCE(0);
	if (cd)
		ChannelTraffic[Channel] = ChannelTraffic[Channel]+1;
	Channel++;
	if (Channel > 127)
	{
		Channel = 1;
		var row=0;
		var column=0;
		while(row < 8)
		{
			column = 0;
			while(column < 16)
			{
				process.stdout.write(ChannelTraffic[row*16+column]+" ");
				column++;
			}
			console.log(" ");
			row++;
			
		}
		console.log("-----------------------------------------");
		//console.log(ChannelTraffic);
	}
	setTimeout(poll,50);
}


var Channel=0;
while(Channel < 128)
{
	ChannelTraffic[Channel]=0;
	Channel++;
}
Channel = 0;
NRFinit();
NRFSetRXAddress0(5,DESTINATION_ADDRESS); // set auto ack address = destination
NRFSetTXAddress(5,DESTINATION_ADDRESS);	 // set destination address
NRFSetRXAddress1(5,SOURCE_ADDRESS);		 // set source address
console.log("Switching to RX mode");
NRFFlushTX();
NRFFlushRX();
NRFEnableRXMode();
NRFWriteRegister(0x05,Channel); 		// select channel 
NRFWriteCE(1);
printRegisters();
poll();

A radio link between the LPC1114 and an Intel Galileo Gen 2 using the NRF24L01

I finally got a link set up between an Intel Galileo Gen 2 and an LPC1114 using the very low cost and low energy NRF24L01 radio modules. The interface code is written from the ground up and is in C for the LPC1114 and javascript for the Galileo. Both code bases were developed in parallel so function names and semantics are common to both.

lpc1114ToNRF
galileoToNRF
Full details are available here:
http://eleceng.dit.ie/frank/arm/BareMetalLPC1114/nrf24l01/index.html