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