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