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

Written by 2bndy5 in 2020

A simple example of sending data from 1 nRF24L01 transceiver to another.

This example was written to be used on 2 devices acting as "nodes". 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
13#include <cmath> // abs()
14#include <ctime> // time()
15#include <cstring> // strcmp()
16#include <iostream> // cin, cout, endl
17#include <string> // string, getline()
18#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
19#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
20
21using namespace std;
22
23/****************** Linux ***********************/
24// Radio CE Pin, CSN Pin, SPI Speed
25// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
26// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
27// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
28#define CSN_PIN 0
29#ifdef MRAA
30 #define CE_PIN 15 // GPIO22
31#elif defined(RF24_WIRINGPI)
32 #define CE_PIN 3 // 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 https://github.com/eclipse/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 sending 32 payloads each containing
44// 32 bytes of data that looks like ASCII art when printed to the serial
45// monitor. The TX node and RX node needs only a single 32 byte buffer.
46#define SIZE 32 // this is the maximum for this example. (minimum is 1)
47char buffer[SIZE + 1]; // for the RX node
48unsigned int counter = 0; // for counting the number of received payloads
49void makePayload(uint8_t); // prototype to construct a payload dynamically
50void setRole(); // prototype to set the node's role
51void master(); // prototype of the TX node's behavior
52void slave(); // prototype of the RX node's behavior
53void printHelp(string); // prototype to function that explain CLI arg usage
54
55// custom defined timer for evaluating transmission time in microseconds
56struct timespec startTimer, endTimer;
57uint32_t getMicros(); // prototype to get elapsed time in microseconds
58
59int main(int argc, char** argv)
60{
61
62 // perform hardware check
63 if (!radio.begin()) {
64 cout << "radio hardware is not responding!!" << endl;
65 return 0; // quit now
66 }
67
68 // add a NULL terminating 0 for printing as a c-string
69 buffer[SIZE] = 0;
70
71 // Let these addresses be used for the pair of nodes used in this example
72 uint8_t address[2][6] = {"1Node", "2Node"};
73 // the TX address^ , ^the RX address
74 // It is very helpful to think of an address as a path instead of as
75 // an identifying device destination
76
77 // to use different addresses on a pair of radios, we need a variable to
78 // uniquely identify which address this radio will use to transmit
79 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
80
81 bool foundArgNode = false;
82 bool foundArgRole = false;
83 bool role = false;
84 if (argc > 1) {
85 // CLI args are specified
86 if ((argc - 1) % 2 != 0) {
87 // some CLI arg doesn't have an option specified for it
88 printHelp(string(argv[0])); // all args need an option in this example
89 return 0;
90 }
91 else {
92 // iterate through args starting after program name
93 int a = 1;
94 while (a < argc) {
95 bool invalidOption = false;
96 if (strcmp(argv[a], "-n") == 0 || strcmp(argv[a], "--node") == 0) {
97 // "-n" or "--node" has been specified
98 foundArgNode = true;
99 if (argv[a + 1][0] - 48 <= 1) {
100 radioNumber = (argv[a + 1][0] - 48) == 1;
101 }
102 else {
103 // option is invalid
104 invalidOption = true;
105 }
106 }
107 else if (strcmp(argv[a], "-r") == 0 || strcmp(argv[a], "--role") == 0) {
108 // "-r" or "--role" has been specified
109 foundArgRole = true;
110 if (argv[a + 1][0] - 48 <= 1) {
111 role = (argv[a + 1][0] - 48) == 1;
112 }
113 else {
114 // option is invalid
115 invalidOption = true;
116 }
117 }
118 if (invalidOption) {
119 printHelp(string(argv[0]));
120 return 0;
121 }
122 a += 2;
123 } // while
124 if (!foundArgNode && !foundArgRole) {
125 // no valid args were specified
126 printHelp(string(argv[0]));
127 return 0;
128 }
129 } // else
130 } // if
131
132 // print example's name
133 cout << argv[0] << endl;
134
135 if (!foundArgNode) {
136 // Set the radioNumber via the terminal on startup
137 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
138 string input;
139 getline(cin, input);
140 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
141 }
142
143 // save on transmission time by setting the radio to only transmit the
144 // number of bytes we need to transmit a float
145 radio.setPayloadSize(SIZE); // default value is the maximum 32 bytes
146
147 // Set the PA Level low to try preventing power supply related problems
148 // because these examples are likely run with nodes in close proximity to
149 // each other.
150 radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
151
152 // set the TX address of the RX node into the TX pipe
153 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
154
155 // set the RX address of the TX node into a RX pipe
156 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
157
158 // For debugging info
159 // radio.printDetails(); // (smaller) function that prints raw register values
160 // radio.printPrettyDetails(); // (larger) function that prints human readable data
161
162 // ready to execute program now
163 if (!foundArgRole) { // if CLI arg "-r"/"--role" was not specified
164 setRole(); // calls master() or slave() based on user input
165 }
166 else { // if CLI arg "-r"/"--role" was specified
167 role ? master() : slave(); // based on CLI arg option
168 }
169 return 0;
170}
171
176void setRole()
177{
178 string input = "";
179 while (!input.length()) {
180 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
181 cout << "*** PRESS 'R' to begin receiving from the other node\n";
182 cout << "*** PRESS 'Q' to exit" << endl;
183 getline(cin, input);
184 if (input.length() >= 1) {
185 if (input[0] == 'T' || input[0] == 't')
186 master();
187 else if (input[0] == 'R' || input[0] == 'r')
188 slave();
189 else if (input[0] == 'Q' || input[0] == 'q')
190 break;
191 else
192 cout << input[0] << " is an invalid input. Please try again." << endl;
193 }
194 input = ""; // stay in the while loop
195 } // while
196} // setRole()
197
201void master()
202{
203 radio.stopListening(); // put radio in TX mode
204
205 unsigned int failures = 0; // keep track of failures
206 uint8_t i = 0;
207 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
208 while (i < SIZE) {
209 makePayload(i);
210 if (!radio.writeFast(&buffer, SIZE)) {
211 failures++;
212 radio.reUseTX();
213 }
214 else {
215 i++;
216 }
217
218 if (failures >= 100) {
219 // most likely no device is listening for the data stream
220 cout << "Too many failures detected. ";
221 cout << "Aborting at payload " << buffer[0];
222 break;
223 }
224 } // while
225
226 uint32_t elapsedTime = getMicros(); // end the timer
227 cout << "Time to transmit data = ";
228 cout << elapsedTime; // print the timer result
229 cout << " us. " << failures; // print number of retries
230 cout << " failures detected. Leaving TX role." << endl;
231} // master
232
236void slave()
237{
238
239 counter = 0;
240 radio.startListening(); // put radio in RX mode
241 time_t startTimer = time(nullptr); // start a timer
242 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
243 if (radio.available()) { // is there a payload
244 radio.read(&buffer, SIZE); // fetch payload from FIFO
245 cout << "Received: " << buffer; // print the payload's value
246 cout << " - " << counter << endl; // print the counter
247 counter++; // increment counter
248 startTimer = time(nullptr); // reset timer
249 }
250 }
251 radio.stopListening(); // use TX mode for idle behavior
252
253 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
254}
255
260void makePayload(uint8_t i)
261{
262
263 // let the first character be an identifying alphanumeric prefix
264 // this lets us see which payload didn't get received
265 buffer[0] = i + (i < 26 ? 65 : 71);
266 for (uint8_t j = 0; j < SIZE - 1; ++j) {
267 char chr = j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - i);
268 chr |= j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - i);
269 buffer[j + 1] = chr + 48;
270 }
271}
272
276uint32_t getMicros()
277{
278 // this function assumes that the timer was started using
279 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
280
281 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
282 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
283 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
284
285 return ((seconds)*1000 + useconds) + 0.5;
286}
287
291void printHelp(string progName)
292{
293 cout << "usage: " << progName << " [-h] [-n {0,1}] [-r {0,1}]\n\n"
294 << "A simple example of streaming data from 1 nRF24L01 transceiver to another.\n"
295 << "\nThis example was written to be used on 2 devices acting as 'nodes'.\n"
296 << "\noptional arguments:\n -h, --help\t\tshow this help message and exit\n"
297 << " -n {0,1}, --node {0,1}\n\t\t\tthe identifying radio number\n"
298 << " -r {0,1}, --role {0,1}\n\t\t\t'1' specifies the TX role."
299 << " '0' specifies the RX role." << endl;
300}
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition RF24.h:116
@ RF24_PA_LOW
Definition RF24.h:50