Examples

The example application shown below is for a debug station.  The debug station receives a radio packet, echos its contents to the serial terminal, and replies with an acknowledgement packet.  Note that the acknowledgement packet is different from an auto-ack packet: the radio sends an auto-ack behind the scenes.  For more information on this and other details on using the radio, see the radio entry in my mechatronics lab guide.

Arduino and AVR versions of the radio code can be found at my Google Code repository. Miguel Moreto kindly provided Arduino sketches for these examples, which can also be found in the SVN repository.

packet.h

Programs that communicate with each other over the radio must use a common packet.h header.  This header is how they share the structure of expected packets.  Here’s an example defining two possible packet types, the MESSAGE packet and the ACK packet:

#ifndef PACKET_H_
#define PACKET_H_
 
#include 
 
/*****			Add labels for the packet types to the enumeration			*****/
 
typedef enum _pt
{
	MESSAGE,
	ACK,
} PACKET_TYPE;
 
/*****			Construct payload format structures			*****/
 
// structures must be 29 bytes long or less.
 
// structure for the MESSAGE packet type
typedef struct _msg
{
	uint8_t messageid;
	uint8_t address[5];
	uint8_t messagecontent[23];
} pf_message_t;
 
// structure for the ACK packet type
typedef struct _ack
{
	uint8_t messageid;
} pf_ack_t;
 
/*****			Add format structures to the union				*****/
 
/// The application-dependent packet format.  Add structures to the union that correspond to the packet types defined
/// in the PACKET_TYPE enumeration.  The format structures may not be more than 29 bytes long.  The _filler array is
/// included to ensure that the union is exactly 29 bytes long.
typedef union _pf
{
	uint8_t _filler[29];	// makes sure the packet will be exactly 32 bytes long - this array should not be accessed directly.
	pf_message_t message;
	pf_ack_t ack;
} payloadformat_t;
 
/*****			Leave the radiopacket_t structure alone.			*****/
 
typedef struct _rp
{
	PACKET_TYPE type;
	uint16_t timestamp;
	payloadformat_t payload;
} radiopacket_t;
 
#endif /* PACKET_H_ */

receiver.cpp

I’ll use C-style code for the example programs, with Arduino function calls. Assume that the project is linked against the Arduino libraries. I use Arduino pin 13 as an LED output; on the Arduino Uno (and similar boards), you can’t use the LED on pin 13 because the radio uses it for SPI communications (the SPI clock pin is hardwired to pin 13). If you’re using an Arduino Uno/Duemilanove/Pro/etc. then remove the writes to pin 13.

The following program will wait until it receives a MESSAGE packet, then respond to the sender with an ACK:

#include 
#include "arduino/Arduino.h"
#include "radio/radio.h"
 
uint8_t station_addr[5] = { 0xE4, 0xE4, 0xE4, 0xE4, 0xE4 };
 
// this must be volatile because it's used in an ISR (radio_rxhandler).
volatile uint8_t rxflag = 0;
 
radiopacket_t packet;
 
int main()
{
	// string buffer for printing to UART
	char output[128];
	sei();
	init();
 
	// start the serial output module
	Serial.begin(100000);
	pinMode(13, OUTPUT);
	pinMode(10, OUTPUT);	// radio's Vcc pin is connected to digital pin 10.
 
	// reset the radio
	digitalWrite(10, LOW);
	delay(100);
	digitalWrite(10, HIGH);
	delay(100);
 
	// initialize the radio, including the SPI module
	Radio_Init();
 
	// configure the receive settings for radio pipe 0
	Radio_Configure_Rx(RADIO_PIPE_0, station_addr, ENABLE);
	// configure radio transciever settings.
	Radio_Configure(RADIO_2MBPS, RADIO_HIGHEST_POWER);
 
	// print a message to UART to indicate that the program has started up
	snprintf(output, 128, "STATION START\n\r");
	Serial.print(output);
 
	for (;;)
	{
		if (rxflag)
		{
			if (Radio_Receive(&packet) != RADIO_RX_MORE_PACKETS)
			{
				// if there are no more packets on the radio, clear the receive flag;
				// otherwise, we want to handle the next packet on the next loop iteration.
				rxflag = 0;
			}
 
			// This station is only expecting to receive MESSAGE packets.
			if (packet.type != MESSAGE)
			{
				snprintf(output, 128, "Error: wrong packet type (type %d).\n\r", packet.type);
				Serial.print(output);
				continue;
			}
 
			// Set the transmit address to the one specified in the message packet.
			Radio_Set_Tx_Addr(packet.payload.message.address);
 
			// Print out the message, along with the message ID and sender address.
			snprintf(output, 128, "Message ID %d from 0x%.2X%.2X%.2X%.2X%.2X: '%s'\n\r",
					packet.payload.message.messageid,
					packet.payload.message.address[0],
					packet.payload.message.address[1],
					packet.payload.message.address[2],
					packet.payload.message.address[3],
					packet.payload.message.address[4],
					packet.payload.message.messagecontent);
			Serial.print(output);
 
			// Reply to the sender by sending an ACK packet.
			packet.type = ACK;
 
			if (Radio_Transmit(&packet, RADIO_WAIT_FOR_TX) == RADIO_TX_MAX_RT)
			{
				snprintf(output, 128, "Could not reply to sender.\n\r");
				Serial.print(output);
			}
			else
			{
				snprintf(output, 128, "Replied to sender.\n\r");
				Serial.print(output);
			}
			// delay for a bit to give a visible flash on the LED.
			delay(50);
			digitalWrite(13, LOW);
		}
	}
 
	return 0;
}
 
/**
 * This function is a hook into the radio's ISR.  It is called whenever the radio generates an RX_DR (received data ready) interrupt.
 */
void radio_rxhandler(uint8_t pipenumber)
{
	// just set a flag and toggle an LED.  The flag is polled in the main function.
	rxflag = 1;
	digitalWrite(13, HIGH);
}

transmitter.cpp

Finally, this program will send a single message packet to the debug station and read back the acknowledgement. Once it receives the ACK packet it will spin in an infinite loop until the microcontroller is reset (remember on AVR the main function should never return).

N.b. you should always read a packet out of the radio, even if you don’t use its contents. Otherwise the radio won’t assert any more interrupts when it receives new packets (it assumes you know there are data in the receive FIFO, so it doesn’t tell you that there are more).

To run this program, make sure there is a station running the receiver.cpp program. Run the transmit.cpp program. The receiver should print the message to its serial port, and send an ACK reply. The transmitter should get the ACK, and the receiver should print out a message indicating that the ACK was successfully sent back.

#include "arduino/Arduino.h"
#include "radio/radio.h"
#include 
 
volatile uint8_t rxflag = 0;
 
// packets are transmitted to this address
uint8_t station_addr[5] = { 0xE4, 0xE4, 0xE4, 0xE4, 0xE4 };
 
// this is this radio's address
uint8_t my_addr[5] = { 0x98, 0x76, 0x54, 0x32, 0x10 };
 
radiopacket_t packet;
 
int main()
{
	init();
	pinMode(13, OUTPUT);
	pinMode(10, OUTPUT);
 
	digitalWrite(10, LOW);
	delay(100);
	digitalWrite(10, HIGH);
	delay(100);
 
	Radio_Init();
 
	// configure the receive settings for radio pipe 0
	Radio_Configure_Rx(RADIO_PIPE_0, my_addr, ENABLE);
	// configure radio transceiver settings.
	Radio_Configure(RADIO_2MBPS, RADIO_HIGHEST_POWER);
 
	// put some data into the packet
	packet.type = MESSAGE;
	memcpy(packet.payload.message.address, my_addr, RADIO_ADDRESS_LENGTH);
	packet.payload.message.messageid = 55;
	snprintf((char*)packet.payload.message.messagecontent, sizeof(packet.payload.message.messagecontent), "Test message.");
 
	// The address to which the next transmission is to be sent
	Radio_Set_Tx_Addr(station_addr);
 
	// send the data
	Radio_Transmit(&packet, RADIO_WAIT_FOR_TX);
 
	// wait for the ACK reply to be transmitted back.
	for (;;)
	{
		if (rxflag)
		{
			// remember always to read the packet out of the radio, even
			// if you don't use the data.
			if (Radio_Receive(&packet) != RADIO_RX_MORE_PACKETS)
			{
				// if there are no more packets on the radio, clear the receive flag;
				// otherwise, we want to handle the next packet on the next loop iteration.
				rxflag = 0;
			}
			if (packet.type == ACK)
			{
				digitalWrite(13, HIGH);
			}
		}
	}
	for (;;);
	return 0;
}
 
void radio_rxhandler(uint8_t pipe_number)
{
	rxflag = 1;
}

15 thoughts on “Examples

  1. Mike

    Thanks for the examples.

    I tried out this example using two NRF24L01+ modules from eBay and an Arduino pro mini And arduino nano.

    I modified the example to correct some small issues – the “listener” code was missing some definitions and a function from the “sender” example. Also the “sender” code had a continue outside of a loop. I also modified the library to change the pin mappings to match those in the Mirf library.

    I wired it using pins MISO, MOSI, SCK, CSN and CE. I also confirmed my setup worked using the Mirf ping examples.

    However I couldn’t get this example to work – I suspect it may be something to do with the SPI side of things. When using the mirf ping code the inbuild LED on pin 13 varies in brightness depending on which state the program is in. In these examples the led is just dead..

    Any ideas?

    Reply
    1. Neil MacMillan Post author

      Hah, thanks for catching those errors. I copied and pasted incautiously.

      I will double-check this code in a day or two. Make sure that you’re using the Arduino version of the code, and that the correct SPI pins (10 to 13 instead of 51 to 53) are selected in spi.cpp.

      In the meantime, if you’d like I can send the original code that actually works (it’s the same but without the Arduino-style setup/loop structure) (email at gmail, neilrqm).

      Reply
  2. Zaigham

    hi;
    i want to use thus nrf2410l chip to transmit a byte of data (at a time).kindly guide me.
    data is available on a micro controller port from where it is to be picked,transmitted and then received.
    im not using any audrino thing. just nrf module and micro controller.
    kindly guide me

    Reply
    1. Neil MacMillan Post author

      The above examples should work on any AVR microcontroller, not just Arduino. The driver code may require some modification for different microcontrollers.

      Reply
  3. Steve Miller

    Thank you for the excellent write up and example code. I tried porting the transmitter code. However, it seems to hang in the wait for ACK loop. I do not see how it can get out of the for loop. There is no break to exit this infinite loop. What am I missing?

    Reply
    1. Neil MacMillan Post author

      The transmitter code just sends one packet and waits for the response. The purpose of that infinite loop once the ACK is received is to spin until the transmitter is reset; so you are correct, it’s not expected to exit. (It seemed sensible when I wrote it.)

      Thank you for pointing out the problem, I’ll update the text to clarify.

      Reply
      1. Steve Miller

        Thank for the super quick response and explanation. I guess what was confusing was the second infinite loop after the ACK test.

        Reply
        1. Neil MacMillan Post author

          Oh yeah, I always stick that into my AVR programs as a robustness thing. If you ever remove the “for” statement on the first infinite loop so that the block contents only run once (e.g. during testing), the second (empty) infinite loop guards the main function from returning. Normally the empty loop should never run.

          Reply
  4. Steve Miller

    FYI, I stumbled on an issue when attempting a hybrid system. The transmitter is an AVR Mega32U2. The receiver is an Arduino. I can send data, but it appeared that somewhere the first byte of the message was being lost. I fought this for a couple of hours before finding out that the code compiled for the Mega32U2 was using a uint8_t for the enum that represents PACKET_TYPE. However, the Arduino appears to be using a uint16_t for this same enum. Therefore, on the receiving side, the Arduino has taking the first byte of the payload and using it as part of the timestamp.

    Communication of Arduino to Arduino may work, although it may send too large a data field. The Arduino IDE is version 1.0.

    Reply
  5. Michael

    Can someone help me with a code for a simple Atmega (32 or 48) not Arduino, would be helpful
    I use WinAVR to write code and to charge the Atmega.

    Reply
  6. Artem Kolesnikov

    Hi, Neil MacMillan!
    Thank you for your good article!
    I use ATMega8, this radio transmitter, but I have a problem – packet loss by 90% (to be delivered only 5-10%). I send a package, and do not get an ACK. Sometimes packages are getting through. You do not know why this might happen? I thought the noise. To test I cut WiFi, closed receiver and transmitter in a microwave oven. It does not solve the problem. In Russia, I asked this question in the forums, no one knows the answer. NRF comes with the radio module. I bought on ebay.

    Reply
    1. Neil MacMillan Post author

      I don’t know either. It sounds like an antenna problem. Maybe the antenna is the wrong impedance or it’s too close to ground and it’s getting filtered. If it has a built-in antenna then it’s probably not an antenna problem though. Make sure the channels match, I could see it getting some crosstalk if the channels are off by one and you’re transmitting at the 2 Mbps data rate. It could also be a battery problem, if the radio draws too much power when it transmits then it could drop the voltage going into the radio and interfere with the transmission. You could try powering it from an AC adapter, or put a big capacitor in parallel with the power supply. All those ideas are grasping at straws.

      Reply
      1. Artem Kolesnikov

        Thank you very much, I will experiment with the power supply. I’ll try and change channels. In the end, I’ll make them work.

        Reply
  7. Pingback: Part 1 Final Report | csc460 Project – jeff and colin

Leave a Reply

Your email address will not be published. Required fields are marked *

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.