Optimized high speed nRF24L01+ driver class documentation v1.4.8
TMRh20 2020 - Optimized fork of the nRF24L01+ driver
Loading...
Searching...
No Matches
examples_linux/multiceiverDemo.cpp

Written by 2bndy5 in 2020

A simple example of sending data from as many as 6 nRF24L01 transceivers to 1 receiving transceiver. This technique is trademarked by Nordic Semiconductors as "MultiCeiver".

This example was written to be used on up to 6 devices acting as TX nodes & only 1 device acting as the RX node (that's a maximum of 7 devices). Use ctrl+c to quit at any time.

1/*
2 * See documentation at https://nRF24.github.io/RF24
3 * See License information at root directory of this library
4 * Author: Brendan Doherty (2bndy5)
5 */
6
16#include <ctime> // time()
17#include <cstring> // strcmp()
18#include <iostream> // cin, cout, endl
19#include <string> // string, getline()
20#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
21#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
22
23using namespace std;
24
25/****************** Linux ***********************/
26// Radio CE Pin, CSN Pin, SPI Speed
27// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
28// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
29// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
30#define CSN_PIN 0
31#ifdef MRAA
32 #define CE_PIN 15 // GPIO22
33#else
34 #define CE_PIN 22
35#endif
36// Generic:
37RF24 radio(CE_PIN, CSN_PIN);
38/****************** Linux (BBB,x86,etc) ***********************/
39// See http://nRF24.github.io/RF24/pages.html for more information on usage
40// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
41// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
42
43// For this example, we'll be using 6 addresses; 1 for each TX node
44// It is very helpful to think of an address as a path instead of as
45// an identifying device destination
46// Notice that the last byte is the only byte that changes in the last 5
47// addresses. This is a limitation of the nRF24L01 transceiver for pipes 2-5
48// because they use the same first 4 bytes from pipe 1.
49uint64_t address[6] = {0x7878787878LL,
50 0xB3B4B5B6F1LL,
51 0xB3B4B5B6CDLL,
52 0xB3B4B5B6A3LL,
53 0xB3B4B5B60FLL,
54 0xB3B4B5B605LL};
55
56// For this example, we'll be using a payload containing
57// a node ID number and a single integer number that will be incremented
58// on every successful transmission.
59// Make a data structure to use as a payload.
60struct PayloadStruct
61{
62 unsigned int nodeID;
63 unsigned int payloadID;
64};
65PayloadStruct payload;
66
67void setRole(); // prototype to set the node's role
68void master(unsigned int); // prototype of a TX node's behavior
69void slave(); // prototype of the RX node's behavior
70void printHelp(string); // prototype to function that explain CLI arg usage
71
72// custom defined timer for evaluating transmission time in microseconds
73struct timespec startTimer, endTimer;
74uint32_t getMicros(); // prototype to get elapsed time in microseconds
75
76int main(int argc, char** argv)
77{
78
79 // perform hardware check
80 if (!radio.begin()) {
81 cout << "radio hardware is not responding!!" << endl;
82 return 0; // quit now
83 }
84
85 // to use different addresses on a pair of radios, we need a variable to
86 // uniquely identify which address this radio will use to transmit
87 unsigned int nodeNumber = 'R'; // integers 0-5 = TX node; character 'R' or integer 82 = RX node
88
89 bool foundArgNode = false;
90 if (argc > 1) {
91 if ((argc - 1) != 2) {
92 // CLI arg "-n"/"--node" needs an option specified for it
93 // only 1 arg is expected, so only traverse the first "--arg option" pair
94 printHelp(string(argv[0]));
95 return 0;
96 }
97 else if (strcmp(argv[1], "-n") == 0 || strcmp(argv[1], "--node") == 0) {
98 // "-n" or "--node" has been specified
99 foundArgNode = true;
100 if ((argv[2][0] - 48) < 6 && (argv[2][0] - 48) >= 0) {
101 nodeNumber = argv[2][0] - 48;
102 }
103 else if (argv[2][0] == 'R' || argv[2][0] == 'r') {
104 nodeNumber = 'R';
105 }
106 else {
107 printHelp(string(argv[0]));
108 return 0;
109 }
110 }
111 else {
112 // "-n"/"--node" arg was not specified
113 printHelp(string(argv[0]));
114 return 0;
115 }
116 }
117
118 // print example's name
119 cout << argv[0] << endl;
120
121 // Set the PA Level low to try preventing power supply related problems
122 // because these examples are likely run with nodes in close proximity to
123 // each other.
124 radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
125
126 // save on transmission time by setting the radio to only transmit the
127 // number of bytes we need to transmit a float
128 radio.setPayloadSize(sizeof(payload)); // 2x int datatype occupy 8 bytes
129
130 // For debugging info
131 // radio.printDetails(); // (smaller) function that prints raw register values
132 // radio.printPrettyDetails(); // (larger) function that prints human readable data
133
134 // ready to execute program now
135 if (!foundArgNode) {
136 setRole(); // calls master() or slave() based on user input
137 }
138 else {
139 nodeNumber < 6 ? master(nodeNumber) : slave();
140 }
141 return 0;
142}
143
148void setRole()
149{
150
151 string input = "";
152 while (!input.length()) {
153 cout << "*** Enter a number between 0 and 5 (inclusive) to act as\n";
154 cout << " a unique node number that transmits to the RX node.\n";
155 cout << "*** PRESS 'R' to begin receiving from the other nodes\n";
156 cout << "*** PRESS 'Q' to exit" << endl;
157 getline(cin, input);
158 if (input.length() >= 1) {
159 unsigned int toNumber = (unsigned int)(input[0]) - 48;
160 if (toNumber < 6 && toNumber >= 0)
161 master(toNumber);
162 else if (input[0] == 'R' || input[0] == 'r')
163 slave();
164 else if (input[0] == 'Q' || input[0] == 'q')
165 break;
166 else
167 cout << input[0] << " is an invalid input. Please try again." << endl;
168 }
169 input = ""; // stay in the while loop
170 } // while
171} // setRole
172
176void master(unsigned int role)
177{
178 // set the payload's nodeID & reset the payload's identifying number
179 payload.nodeID = role;
180 payload.payloadID = 0;
181
182 // Set the address on pipe 0 to the RX node.
183 radio.stopListening(); // put radio in TX mode
184 radio.openWritingPipe(address[role]);
185
186 // According to the datasheet, the auto-retry features's delay value should
187 // be "skewed" to allow the RX node to receive 1 transmission at a time.
188 // So, use varying delay between retry attempts and 15 (at most) retry attempts
189 radio.setRetries(((role * 3) % 12) + 3, 15); // maximum value is 15 for both args
190
191 unsigned int failures = 0;
192 while (failures < 6) {
193 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
194 bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
195 uint32_t timerElapsed = getMicros(); // end the timer
196
197 if (report) {
198 // payload was delivered
199 cout << "Transmission of PayloadID ";
200 cout << payload.payloadID; // print payload number
201 cout << " as node " << payload.nodeID; // print node number
202 cout << " successful! Time to transmit = ";
203 cout << timerElapsed << " us" << endl; // print the timer result
204 }
205 else {
206 // payload was not delivered
207 failures++;
208 cout << "Transmission failed or timed out" << endl;
209 }
210 payload.payloadID++; // increment payload number
211
212 // to make this example readable in the terminal
213 delay(1000); // slow transmissions down by 1 second
214 } // while
215 cout << failures << " failures detected. Leaving TX role." << endl;
216} // master
217
221void slave()
222{
223
224 // Set the addresses for all pipes to TX nodes
225 for (uint8_t i = 0; i < 6; ++i)
226 radio.openReadingPipe(i, address[i]);
227
228 radio.startListening(); // put radio in RX mode
229
230 time_t startTimer = time(nullptr); // start a timer
231 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
232 uint8_t pipe;
233 if (radio.available(&pipe)) { // is there a payload? get the pipe number that recieved it
234 uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
235 radio.read(&payload, bytes); // fetch payload from FIFO
236 cout << "Received " << (unsigned int)bytes; // print the size of the payload
237 cout << " bytes on pipe " << (unsigned int)pipe; // print the pipe number
238 cout << " from node " << payload.nodeID; // print the payload's origin
239 cout << ". PayloadID: " << payload.payloadID << endl; // print the payload's number
240 startTimer = time(nullptr); // reset timer
241 }
242 }
243 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
244} // slave
245
249uint32_t getMicros()
250{
251 // this function assumes that the timer was started using
252 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
253
254 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
255 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
256 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
257
258 return ((seconds)*1000 + useconds) + 0.5;
259}
260
264void printHelp(string progName)
265{
266 cout << "usage: " << progName << " [-h] [-n {0,1,2,3,4,5,r,R}]\n\n"
267 << "A simple example of sending data from as many as 6 nRF24L01 transceivers to\n"
268 << "1 receiving transceiver. This technique is trademarked by\n"
269 << "Nordic Semiconductors as 'MultiCeiver'.\n"
270 << "\nThis example was written to be used on up to 6 devices acting as TX nodes with\n"
271 << "another device acting as a RX node (that's a total of 7 devices).\n"
272 << "\noptional arguments:\n -h, --help\t\tshow this help message and exit\n"
273 << " -n {0,1,2,3,4,5,r,R}, --node {0,1,2,3,4,5,r,R}"
274 << "\n\t\t\t0-5 specifies the identifying node ID number for the TX role."
275 << "\n\t\t\t'r' or 'R' specifies the RX role." << endl;
276}
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition: RF24.h:116
@ RF24_PA_LOW
Definition: RF24.h:50
#define delay(milisec)