Linux examples¶
gettingstarted.cpp¶
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, but remember state of the radio will persist until another example or application uses it.
13#include <ctime> // time()
14#include <iostream> // cin, cout, endl
15#include <string> // string, getline()
16#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
17#include <RF24Revamped/RF24Revamped.h> // RF24, RF24_PA_LOW, delay()
18
19using namespace std;
20
21/****************** Linux ***********************/
22// Radio CE Pin, CSN Pin, SPI Speed
23// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
24// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
25// ie: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
26
27// Generic:
28RF24Revamped radio(22, 0);
29/****************** Linux (BBB,x86,etc) ***********************/
30// See http://2bndy5.github.io/RF24/pages.html for more information on usage
31// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
32// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
33
34// For this example, we'll be using a payload containing
35// a single float number that will be incremented
36// on every successful transmission
37float payload = 0.0;
38
39void setRole(); // prototype to set the node's role
40void master(); // prototype of the TX node's behavior
41void slave(); // prototype of the RX node's behavior
42
43// custom defined timer for evaluating transmission time in microseconds
44struct timespec startTimer, endTimer;
45uint32_t getMicros(); // prototype to get ellapsed time in microseconds
46
47int main(int argc, char** argv) {
48
49 // perform hardware check
50 if (!radio.begin()) {
51 cout << "radio hardware is not responding!!" << endl;
52 return 0; // quit now
53 }
54
55 // to use different addresses on a pair of radios, we need a variable to
56 // uniquely identify which address this radio will use to transmit
57 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
58
59 // print example's name
60 cout << argv[0] << endl;
61
62 // Let these addresses be used for the pair
63 uint8_t address[2][6] = {"1Node", "2Node"};
64 // It is very helpful to think of an address as a path instead of as
65 // an identifying device destination
66
67 // Set the radioNumber via the terminal on startup
68 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
69 string input;
70 getline(cin, input);
71 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
72
73 // save on transmission time by setting the radio to only transmit the
74 // number of bytes we need to transmit a float
75 radio.setPayloadLength(sizeof(payload)); // float datatype occupies 4 bytes
76
77 // Set the PA Level low to try preventing power supply related problems
78 // because these examples are likely run with nodes in close proximity to
79 // each other.
80 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
81
82 // set the TX address of the RX node into the TX pipe
83 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
84
85 // set the RX address of the TX node into a RX pipe
86 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
87
88 radio.setAutoRetry(4000, 15);
89 // For debugging info
90 radio.printDetails();
91
92 // ready to execute program now
93 setRole(); // calls master() or slave() based on user input
94 return 0;
95}
96
97
98/**
99 * set this node's role from stdin stream.
100 * this only considers the first char as input.
101 */
102void setRole() {
103 string input = "";
104 while (!input.length()) {
105 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
106 cout << "*** PRESS 'R' to begin receiving from the other node\n";
107 cout << "*** PRESS 'Q' to exit" << endl;
108 getline(cin, input);
109 if (input.length() >= 1) {
110 if (input[0] == 'T' || input[0] == 't')
111 master();
112 else if (input[0] == 'R' || input[0] == 'r')
113 slave();
114 else if (input[0] == 'Q' || input[0] == 'q')
115 break;
116 else
117 cout << input[0] << " is an invalid input. Please try again." << endl;
118 }
119 input = ""; // stay in the while loop
120 } // while
121} // setRole()
122
123
124/**
125 * make this node act as the transmitter
126 */
127void master() {
128 radio.stopListening(); // put radio in TX mode
129
130 unsigned int failure = 0; // keep track of failures
131 while (failure < 6) {
132 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
133 bool report = radio.send(&payload, sizeof(float)); // transmit & save the report
134 uint32_t timerEllapsed = getMicros(); // end the timer
135
136 if (report) {
137 // payload was delivered
138 cout << "Transmission successful! ";
139 cout << "Time to transmit = ";
140 cout << timerEllapsed; // print the timer result
141 cout << " us. Sent: " << payload << endl; // print payload sent
142 payload += 0.01; // increment float payload
143
144 } else {
145 // payload was not delivered
146 cout << "Transmission failed or timed out" << endl;
147 failure++;
148 }
149
150 // to make this example readable in the terminal
151 delay(1000); // slow transmissions down by 1 second
152 }
153 cout << failure << " failures detected. Leaving TX role." << endl;
154}
155
156/**
157 * make this node act as the receiver
158 */
159void slave() {
160
161 radio.startListening(); // put radio in RX mode
162
163 time_t startTimer = time(nullptr); // start a timer
164 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
165 if (radio.available()) { // is there a payload?
166 unsigned int pipe = radio.pipe(); // get the pipe number that recieved it
167 unsigned int bytes = radio.any(); // get the size of the payload
168 radio.read(&payload, bytes); // fetch payload from FIFO
169 cout << "Received " << bytes; // print the size of the payload
170 cout << " bytes on pipe " << pipe; // print the pipe number
171 cout << ": " << payload << endl; // print the payload's value
172 startTimer = time(nullptr); // reset timer
173 }
174 }
175 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
176 radio.stopListening();
177}
178
179
180/**
181 * Calculate the ellapsed time in microseconds
182 */
183uint32_t getMicros() {
184 // this function assumes that the timer was started using
185 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
186
187 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
188 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
189 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
190
191 return ((seconds) * 1000 + useconds) + 0.5;
192}
acknowledgementPayloads.cpp¶
A simple example of sending data from 1 nRF24L01 transceiver to another with Acknowledgement (ACK) payloads attached to ACK packets.
This example was written to be used on 2 devices acting as “nodes”.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
14#include <ctime> // time()
15#include <iostream> // cin, cout, endl
16#include <string> // string, getline()
17#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
18#include <RF24Revamped/RF24Revamped.h> // RF24, RF24_PA_LOW, delay()
19
20using namespace std;
21
22/****************** Linux ***********************/
23// Radio CE Pin, CSN Pin, SPI Speed
24// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
25// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
26// ie: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
27
28// Generic:
29RF24Revamped radio(22, 0);
30/****************** Linux (BBB,x86,etc) ***********************/
31// See http://2bndy5.github.io/RF24/pages.html for more information on usage
32// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
33// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
34
35// For this example, we'll be using a payload containing
36// a string & an integer number that will be incremented
37// on every successful transmission.
38// Make a data structure to store the entire payload of different datatypes
39struct PayloadStruct {
40 char message[7]; // only using 6 characters for TX & ACK payloads
41 uint8_t counter;
42};
43PayloadStruct payload;
44
45void setRole(); // prototype to set the node's role
46void master(); // prototype of the TX node's behavior
47void slave(); // prototype of the RX node's behavior
48
49// custom defined timer for evaluating transmission time in microseconds
50struct timespec startTimer, endTimer;
51uint32_t getMicros(); // prototype to get ellapsed time in microseconds
52
53
54int main(int argc, char** argv) {
55 // perform hardware check
56 if (!radio.begin()) {
57 cout << "radio hardware is not responding!!" << endl;
58 return 0; // quit now
59 }
60
61 // Let these addresses be used for the pair
62 uint8_t address[2][6] = {"1Node", "2Node"};
63 // It is very helpful to think of an address as a path instead of as
64 // an identifying device destination
65
66 // to use different addresses on a pair of radios, we need a variable to
67 // uniquely identify which address this radio will use to transmit
68 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
69
70 // print example's name
71 cout << argv[0] << endl;
72
73 // Set the radioNumber via the terminal on startup
74 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
75 string input;
76 getline(cin, input);
77 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
78
79 // to use ACK payloads, we need to enable dynamic payload lengths
80 radio.setDynamicPayloads(true); // ACK payloads are dynamically sized
81
82 // Acknowledgement packets have no payloads by default. We need to enable
83 // this feature for all nodes (TX & RX) to use ACK payloads.
84 radio.enableAckPayload();
85
86 // Set the PA Level low to try preventing power supply related problems
87 // because these examples are likely run with nodes in close proximity to
88 // each other.
89 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
90
91 // set the TX address of the RX node into the TX pipe
92 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
93
94 // set the RX address of the TX node into a RX pipe
95 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
96
97 // For debugging info
98 // radio.printDetails();
99
100 // ready to execute program now
101 setRole(); // calls master() or slave() based on user input
102 return 0;
103}
104
105
106/**
107 * set this node's role from stdin stream.
108 * this only considers the first char as input.
109 */
110void setRole() {
111 string input = "";
112 while (!input.length()) {
113 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
114 cout << "*** PRESS 'R' to begin receiving from the other node\n";
115 cout << "*** PRESS 'Q' to exit" << endl;
116 getline(cin, input);
117 if (input.length() >= 1) {
118 if (input[0] == 'T' || input[0] == 't')
119 master();
120 else if (input[0] == 'R' || input[0] == 'r')
121 slave();
122 else if (input[0] == 'Q' || input[0] == 'q')
123 break;
124 else
125 cout << input[0] << " is an invalid input. Please try again." << endl;
126 }
127 input = ""; // stay in the while loop
128 } // while
129} // setRole()
130
131
132/**
133 * make this node act as the transmitter
134 */
135void master() {
136 memcpy(payload.message, "Hello ", 6); // set the payload message
137 radio.stopListening(); // put radio in TX mode
138
139 unsigned int failures = 0; // keep track of failures
140 while (failures < 6) {
141 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
142 bool report = radio.send(&payload, sizeof(payload)); // transmit & save the report
143 uint32_t timerEllapsed = getMicros(); // end the timer
144
145 if (report) {
146 // payload was delivered
147 cout << "Transmission successful! Time to transmit = ";
148 cout << timerEllapsed; // print the timer result
149 cout << " us. Sent: ";
150 cout << payload.message; // print outgoing message
151 cout << (unsigned int)payload.counter; // print outgoing counter counter
152
153 if (radio.available()) {
154 unsigned int pipe = radio.pipe(); // grab the pipe number that received it
155 unsigned int bytes = radio.any(); // get the size of the payload
156 PayloadStruct received;
157 radio.read(&received, sizeof(received)); // get incoming ACK payload
158 cout << " Received " << bytes; // print incoming payload size
159 cout << " bytes on pipe " << pipe; // print pipe that received it
160 cout << ": " << received.message; // print incoming message
161 cout << (unsigned int)received.counter << endl; // print incoming counter
162 payload.counter = received.counter + 1; // save incoming counter & increment for next outgoing
163 } // if got an ACK payload
164 else {
165 cout << " Received an empty ACK packet." << endl; // ACK had no payload
166 }
167 } // if delivered
168 else {
169 cout << "Transmission failed or timed out" << endl; // payload was not delivered
170 failures++; // increment failures
171 }
172
173 // to make this example readable in the terminal
174 delay(1000); // slow transmissions down by 1 second
175 } // while
176 cout << failures << " failures detected. Leaving TX role." << endl;
177} // master
178
179
180/**
181 * make this node act as the receiver
182 */
183void slave() {
184 memcpy(payload.message, "World ", 6); // set the payload message
185
186 // load the payload for the first received transmission on pipe 0
187 radio.writeAck(1, &payload, sizeof(payload));
188
189 radio.startListening(); // put radio in RX mode
190 time_t startTimer = time(nullptr); // start a timer
191 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
192 if (radio.available()) { // is there a payload?
193 unsigned int pipe = radio.pipe(); // get the pipe number that recieved it
194 unsigned int bytes = radio.any(); // get the size of the payload
195 PayloadStruct received;
196 radio.read(&received, sizeof(received)); // fetch payload from RX FIFO
197 cout << "Received " << bytes; // print the size of the payload
198 cout << " bytes on pipe " << pipe; // print the pipe number
199 cout << ": " << received.message;
200 cout << (unsigned int)received.counter; // print received payload
201 cout << " Sent: ";
202 cout << payload.message;
203 cout << (unsigned int)payload.counter; // print ACK payload sent
204 cout << endl;
205 startTimer = time(nullptr); // reset timer
206
207 // save incoming counter & increment for next outgoing
208 payload.counter = received.counter + 1;
209 // load the payload for the first received transmission on pipe 0
210 radio.writeAck(1, &payload, sizeof(payload));
211 } // if received something
212 } // while
213 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
214 radio.stopListening(); // recommended idle behavior is TX mode
215} // slave
216
217
218/**
219 * Calculate the ellapsed time in microseconds
220 */
221uint32_t getMicros() {
222 // this function assumes that the timer was started using
223 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
224
225 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
226 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
227 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
228
229 return ((seconds) * 1000 + useconds) + 0.5;
230}
manualAcknowledgements.cpp¶
This is a simple example of using the RF24 class on a Raspberry Pi to transmit and respond with acknowledgment (ACK) transmissions. Notice that the auto-ack feature is enabled, but this example doesn’t use automatic ACK payloads because automatic ACK payloads’ data will always be outdated by 1 transmission. Instead, this example uses a call and response paradigm.
This example was written to be used on 2 devices acting as “nodes”.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
19#include <ctime> // time()
20#include <iostream> // cin, cout, endl
21#include <string> // string, getline()
22#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
23#include <RF24Revamped/RF24Revamped.h> // RF24, RF24_PA_LOW, delay()
24
25using namespace std;
26
27/****************** Linux ***********************/
28// Radio CE Pin, CSN Pin, SPI Speed
29// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
30// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
31// ie: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
32
33// Generic:
34RF24Revamped radio(22, 0);
35/****************** Linux (BBB,x86,etc) ***********************/
36// See http://2bndy5.github.io/RF24/pages.html for more information on usage
37// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
38// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
39
40// For this example, we'll be using a payload containing
41// a string & an integer number that will be incremented
42// on every successful transmission.
43// Make a data structure to store the entire payload of different datatypes
44struct PayloadStruct {
45 char message[7]; // only using 6 characters for TX & RX payloads
46 uint8_t counter;
47};
48PayloadStruct payload;
49
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
53
54// custom defined timer for evaluating transmission time in microseconds
55struct timespec startTimer, endTimer;
56uint32_t getMicros(); // prototype to get ellapsed time in microseconds
57
58
59int main(int argc, char** argv) {
60
61 // perform hardware check
62 if (!radio.begin()) {
63 cout << "radio hardware is not responding!!" << endl;
64 return 0; // quit now
65 }
66
67 // append a NULL terminating 0 for printing as a c-string
68 payload.message[6] = 0;
69
70 // Let these addresses be used for the pair of nodes used in this example
71 uint8_t address[2][6] = {"1Node", "2Node"};
72 // the TX address^ , ^the RX address
73 // It is very helpful to think of an address as a path instead of as
74 // an identifying device destination
75
76 // to use different addresses on a pair of radios, we need a variable to
77 // uniquely identify which address this radio will use to transmit
78 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
79
80 // print example's name
81 cout << argv[0] << endl;
82
83 // Set the radioNumber via the terminal on startup
84 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
85 string input;
86 getline(cin, input);
87 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
88
89 // Set the PA Level low to try preventing power supply related problems
90 // because these examples are likely run with nodes in close proximity to
91 // each other.
92 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
93
94 // save on transmission time by setting the radio to only transmit the
95 // number of bytes we need to transmit a float
96 radio.setPayloadLength(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
97
98 // set the TX address of the RX node into the TX pipe
99 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
100
101 // set the RX address of the TX node into a RX pipe
102 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
103
104 // For debugging info
105 // radio.printDetails();
106
107 // ready to execute program now
108 setRole(); // calls master() or slave() based on user input
109 return 0;
110} // main
111
112
113/**
114 * set this node's role from stdin stream.
115 * this only considers the first char as input.
116 */
117void setRole() {
118 string input = "";
119 while (!input.length()) {
120 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
121 cout << "*** PRESS 'R' to begin receiving from the other node\n";
122 cout << "*** PRESS 'Q' to exit" << endl;
123 getline(cin, input);
124 if (input.length() >= 1) {
125 if (input[0] == 'T' || input[0] == 't')
126 master();
127 else if (input[0] == 'R' || input[0] == 'r')
128 slave();
129 else if (input[0] == 'Q' || input[0] == 'q')
130 break;
131 else
132 cout << input[0] << " is an invalid input. Please try again." << endl;
133 }
134 input = ""; // stay in the while loop
135 } // while
136} // setRole()
137
138
139/**
140 * make this node act as the transmitter
141 */
142void master() {
143
144 memcpy(payload.message, "Hello ", 6); // set the outgoing message
145 radio.stopListening(); // put in TX mode
146
147 unsigned int failures = 0; // keep track of failures
148 while (failures < 6) {
149 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
150 bool report = radio.send(&payload, sizeof(payload)); // transmit & save the report
151
152 if (report) {
153 // transmission successful; wait for response and print results
154
155 radio.startListening(); // put in RX mode
156 unsigned long start_timeout = millis(); // timer to detect no response
157 while (!radio.available()) { // wait for response
158 if (millis() - start_timeout > 200) // only wait 200 ms
159 break;
160 }
161 unsigned long ellapsedTime = getMicros(); // end the timer
162 radio.stopListening(); // put back in TX mode
163
164 // print summary of transactions
165 cout << "Transmission successful! ";
166 if (radio.available()) { // is there a payload received?
167 unsigned int pipe = radio.pipe(); // grab the pipe number that received it
168 unsigned int bytes = radio.any(); // grab the incoming payload size
169 cout << "Round trip delay = ";
170 cout << ellapsedTime; // print the timer result
171 cout << " us. Sent: " << payload.message; // print outgoing message
172 cout << (unsigned int)payload.counter; // print outgoing counter
173 PayloadStruct received;
174 radio.read(&received, sizeof(received)); // get incoming payload
175 cout << " Recieved " << bytes; // print incoming payload size
176 cout << " on pipe " << pipe; // print RX pipe number
177 cout << ": " << received.message; // print the incoming message
178 cout << (unsigned int)received.counter; // print the incoming counter
179 cout << endl;
180 payload.counter = received.counter; // save incoming counter for next outgoing counter
181 }
182 else {
183 cout << "Recieved no response." << endl; // no response received
184 }
185 }
186 else {
187 cout << "Transmission failed or timed out"; // payload was not delivered
188 cout << endl;
189 failures++; // increment failure counter
190 } // report
191
192 // to make this example readable in the terminal
193 delay(1000); // slow transmissions down by 1 second
194 } // while
195
196 cout << failures << " failures detected. Leaving TX role." << endl;
197} // master
198
199
200/**
201 * make this node act as the receiver
202 */
203void slave() {
204 memcpy(payload.message, "World ", 6); // set the response message
205 radio.startListening(); // put in RX mode
206
207 time_t startTimer = time(nullptr); // start a timer
208 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
209 if (radio.available()) { // is there a payload?
210 uint8_t pipe = radio.pipe(); // get the pipe number that recieved it
211 uint8_t bytes = radio.any(); // get size of incoming payload
212 PayloadStruct received;
213 radio.read(&received, sizeof(received)); // get incoming payload
214 payload.counter = received.counter + 1; // increment payload for response
215
216 // transmit response & save result to `report`
217 radio.stopListening(); // put in TX mode
218 unsigned long start_response = millis();
219 bool report = radio.send(&payload, sizeof(payload)); // send response
220 while (!report && millis() - start_response < 200) {
221 report = radio.resend(); // retry for 200 ms
222 }
223 radio.startListening(); // put back in RX mode
224
225 // print summary of transactions
226 cout << "Received " << (unsigned int)bytes; // print the size of the payload
227 cout << " bytes on pipe ";
228 cout << (unsigned int)pipe; // print the pipe number
229 cout << ": " << received.message; // print incoming message
230 cout << (unsigned int)received.counter; // print incoming counter
231
232 if (report) {
233 cout << " Sent: " << payload.message; // print outgoing message
234 cout << (unsigned int)payload.counter; // print outgoing counter
235 cout << endl;
236 }
237
238 else {
239 // failed to send response
240 cout << " Response failed to send." << endl;
241 }
242
243 startTimer = time(nullptr); // reset timer
244 } // available
245 } // while
246
247 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
248 radio.stopListening(); // recommended idle mode is TX mode
249} // slave
250
251
252/**
253 * Calculate the ellapsed time in microseconds
254 */
255uint32_t getMicros() {
256 // this function assumes that the timer was started using
257 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
258
259 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
260 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
261 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
262
263 return ((seconds) * 1000 + useconds) + 0.5;
264}
multiceiverDemo.cpp¶
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”.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
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 <RF24Revamped/RF24Revamped.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: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
30
31// Generic:
32RF24Revamped radio(22, 0);
33/****************** Linux (BBB,x86,etc) ***********************/
34// See http://2bndy5.github.io/RF24/pages.html for more information on usage
35// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
36// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
37
38
39// For this example, we'll be using 6 addresses; 1 for each TX node
40// It is very helpful to think of an address as a path instead of as
41// an identifying device destination
42// Notice that the last byte is the only byte that changes in the last 5
43// addresses. This is a limitation of the nRF24L01 transceiver for pipes 2-5
44// because they use the same first 4 bytes from pipe 1.
45uint64_t address[6] = {0x7878787878LL,
46 0xB3B4B5B6F1LL,
47 0xB3B4B5B6CDLL,
48 0xB3B4B5B6A3LL,
49 0xB3B4B5B60FLL,
50 0xB3B4B5B605LL};
51
52
53// For this example, we'll be using a payload containing
54// a node ID number and a single integer number that will be incremented
55// on every successful transmission.
56// Make a data structure to use as a payload.
57struct PayloadStruct
58{
59 unsigned int nodeID;
60 unsigned int payloadID;
61};
62PayloadStruct payload;
63
64void setRole(); // prototype to set the node's role
65void master(unsigned int); // prototype of a TX node's behavior
66void slave(); // prototype of the RX node's behavior
67void printHelp(string); // prototype to function that explain CLI arg usage
68
69// custom defined timer for evaluating transmission time in microseconds
70struct timespec startTimer, endTimer;
71uint32_t getMicros(); // prototype to get ellapsed time in microseconds
72
73
74int main(int argc, char** argv) {
75
76 // perform hardware check
77 if (!radio.begin()) {
78 cout << "radio hardware is not responding!!" << endl;
79 return 0; // quit now
80 }
81
82 // to use different addresses on a pair of radios, we need a variable to
83 // uniquely identify which address this radio will use to transmit
84 unsigned int nodeNumber = 'R'; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
85
86 bool foundArgNode = false;
87 if (argc > 1) {
88 if ((argc - 1) != 2) {
89 // CLI arg "-n"/"--node" needs an option specified for it
90 // only 1 arg is expected, so only traverse the first "--arg option" pair
91 printHelp(string(argv[0]));
92 return 0;
93 }
94 else if (strcmp(argv[1], "-n") == 0 || strcmp(argv[1], "--node") == 0) {
95 // "-n" or "--node" has been specified
96 foundArgNode = true;
97 if ((argv[2][0] - 48) < 6) {
98 nodeNumber = argv[2][0] - 48;
99 }
100 else if (argv[2][0] == 'R' || argv[2][0] == 'r') {
101 nodeNumber = 'R';
102 }
103 else {
104 printHelp(string(argv[0]));
105 return 0;
106 }
107 }
108 else {
109 // "-n"/"--node" arg was not specified
110 printHelp(string(argv[0]));
111 return 0;
112 }
113 }
114
115 // print example's name
116 cout << argv[0] << endl;
117
118 // Set the PA Level low to try preventing power supply related problems
119 // because these examples are likely run with nodes in close proximity to
120 // each other.
121 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
122
123 // save on transmission time by setting the radio to only transmit the
124 // number of bytes we need to transmit a float
125 radio.setPayloadLength(sizeof(payload)); // 2x int datatype occupy 8 bytes
126
127 // For debugging info
128 // radio.printDetails();
129
130 // ready to execute program now
131 if (!foundArgNode) {
132 setRole(); // calls master() or slave() based on user input
133 }
134 else {
135 nodeNumber < 6 ? master(nodeNumber) : slave();
136 }
137 return 0;
138}
139
140
141/**
142 * set this node's role from stdin stream.
143 * this only considers the first char as input.
144 */
145void setRole() {
146
147 string input = "";
148 while (!input.length()) {
149 cout << "*** Enter a number between 0 and 5 (inclusive) to act as\n";
150 cout << " a unique node number that transmits to the RX node.\n";
151 cout << "*** PRESS 'R' to begin receiving from the other nodes\n";
152 cout << "*** PRESS 'Q' to exit" << endl;
153 getline(cin, input);
154 if (input.length() >= 1) {
155 unsigned int toNumber = (unsigned int)(input[0]) - 48;
156 if (toNumber < 6 && toNumber >= 0)
157 master(toNumber);
158 else if (input[0] == 'R' || input[0] == 'r')
159 slave();
160 else if (input[0] == 'Q' || input[0] == 'q')
161 break;
162 else
163 cout << input[0] << " is an invalid input. Please try again." << endl;
164 }
165 input = ""; // stay in the while loop
166 } // while
167} // setRole
168
169
170/**
171 * act as unique TX node identified by the `role` number
172 */
173void master(unsigned int role) {
174 // set the payload's nodeID & reset the payload's identifying number
175 payload.nodeID = role;
176 payload.payloadID = 0;
177
178 // Set the address on pipe 0 to the RX node.
179 radio.stopListening(); // put radio in TX mode
180 radio.openWritingPipe(address[role]);
181
182 // According to the datasheet, the auto-retry features's delay value should
183 // be "skewed" to allow the RX node to receive 1 transmission at a time.
184 // So, use varying delay between retry attempts and 15 (at most) retry attempts
185 radio.setAutoRetry(((role * 3) % 12) + 3, 15); // maximum value is 15 for both args
186
187 unsigned int failures = 0;
188 while (failures < 6) {
189 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
190 bool report = radio.send(&payload, sizeof(payload)); // transmit & save the report
191 uint32_t timerEllapsed = getMicros(); // end the timer
192
193 if (report) {
194 // payload was delivered
195 cout << "Transmission of PayloadID ";
196 cout << payload.payloadID; // print payload number
197 cout << " as node " << payload.nodeID; // print node number
198 cout << " successful! Time to transmit = ";
199 cout << timerEllapsed << " us" << endl; // print the timer result
200 }
201 else {
202 // payload was not delivered
203 failures++;
204 cout << "Transmission failed or timed out" << endl;
205 }
206 payload.payloadID++; // increment payload number
207
208 // to make this example readable in the terminal
209 delay(1000); // slow transmissions down by 0.5 second
210 } // while
211 cout << failures << " failures detected. Leaving TX role." << endl;
212} // master
213
214
215/**
216 * act as the RX node that receives from up to 6 other TX nodes
217 */
218void slave() {
219
220 // Set the addresses for all pipes to TX nodes
221 for (uint8_t i = 0; i < 6; ++i)
222 radio.openReadingPipe(i, address[i]);
223
224 radio.startListening(); // put radio in RX mode
225
226 time_t startTimer = time(nullptr); // start a timer
227 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
228 if (radio.available()) { // is there a payload?
229 unsigned int pipe = radio.pipe(); // get the pipe number that recieved it
230 unsigned int bytes = radio.any(); // get the size of the payload
231 radio.read(&payload, bytes); // fetch payload from FIFO
232 cout << "Received " << bytes; // print the size of the payload
233 cout << " bytes on pipe " << pipe; // print the pipe number
234 cout << " from node " << payload.nodeID; // print the payload's origin
235 cout << ". PayloadID: " << payload.payloadID; // print the payload's number
236 cout << endl;
237 startTimer = time(nullptr); // reset timer
238 }
239 }
240 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
241} // slave
242
243
244/**
245 * Calculate the ellapsed time in microseconds
246 */
247uint32_t getMicros() {
248 // this function assumes that the timer was started using
249 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
250
251 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
252 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
253 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
254
255 return ((seconds) * 1000 + useconds) + 0.5;
256}
257
258
259/**
260 * print a manual page of instructions on how to use this example's CLI args
261 */
262void printHelp(string progName) {
263 cout << "usage: " << progName << " [-h] [-n {0,1,2,3,4,5,r,R}]\n\n"
264 << "A simple example of sending data from as many as 6 nRF24L01 transceivers to\n"
265 << "1 receiving transceiver. This technique is trademarked by\n"
266 << "Nordic Semiconductors as 'MultiCeiver'.\n"
267 << "\nThis example was written to be used on up to 6 devices acting as TX nodes with\n"
268 << "another device acting as a RX node (that's a total of 7 devices).\n"
269 << "\noptional arguments:\n -h, --help\t\tshow this help message and exit\n"
270 << " -n {0,1,2,3,4,5,r,R}, --node {0,1,2,3,4,5,r,R}"
271 << "\n\t\t\t0-5 specifies the identifying node ID number for the TX role."
272 << "\n\t\t\t'r' or 'R' specifies the RX role." << endl;
273}
streamingData.cpp¶
This is a simple example of using the RF24 class on a Raspberry Pi for streaming multiple payloads.
This example was written to be used on 2 devices acting as “nodes”.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
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 <RF24Revamped/RF24Revamped.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: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
28
29// Generic:
30RF24Revamped radio(22, 0);
31/****************** Linux (BBB,x86,etc) ***********************/
32// See http://2bndy5.github.io/RF24/pages.html for more information on usage
33// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
34// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
35
36// For this example, we'll be sending 32 payloads each containing
37// 32 bytes of data that looks like ASCII art when printed to the serial
38// monitor. The TX node and RX node needs only a single 32 byte buffer.
39#define SIZE 32 // this is the maximum for this example. (minimum is 1)
40char buffer[SIZE + 1]; // for the RX node
41unsigned int counter = 0; // for counting the number of received payloads
42void makePayload(uint8_t); // prototype to construct a payload dynamically
43void setRole(); // prototype to set the node's role
44void master(); // prototype of the TX node's behavior
45void slave(); // prototype of the RX node's behavior
46void printHelp(string); // prototype to function that explain CLI arg usage
47
48// custom defined timer for evaluating transmission time in microseconds
49struct timespec startTimer, endTimer;
50uint32_t getMicros(); // prototype to get ellapsed time in microseconds
51
52
53int main(int argc, char** argv) {
54
55 // perform hardware check
56 if (!radio.begin()) {
57 cout << "radio hardware is not responding!!" << endl;
58 return 0; // quit now
59 }
60
61 // add a NULL terminating 0 for printing as a c-string
62 buffer[SIZE] = 0;
63
64 // Let these addresses be used for the pair of nodes used in this example
65 uint8_t address[2][6] = {"1Node", "2Node"};
66 // the TX address^ , ^the RX address
67 // It is very helpful to think of an address as a path instead of as
68 // an identifying device destination
69
70 // to use different addresses on a pair of radios, we need a variable to
71 // uniquely identify which address this radio will use to transmit
72 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
73
74 bool foundArgNode = false;
75 bool foundArgRole = false;
76 bool role = false;
77 if (argc > 1) {
78 // CLI args are specified
79 if ((argc - 1) % 2 != 0) {
80 // some CLI arg doesn't have an option specified for it
81 printHelp(string(argv[0])); // all args need an option in this example
82 return 0;
83 }
84 else {
85 // iterate through args starting after program name
86 int a = 1;
87 while (a < argc) {
88 bool invalidOption = false;
89 if (strcmp(argv[a], "-n") == 0 || strcmp(argv[a], "--node") == 0) {
90 // "-n" or "--node" has been specified
91 foundArgNode = true;
92 if (argv[a + 1][0] - 48 <= 1) {
93 radioNumber = (argv[a + 1][0] - 48) == 1;
94 }
95 else {
96 // option is invalid
97 invalidOption = true;
98 }
99 }
100 else if (strcmp(argv[a], "-r") == 0 || strcmp(argv[a], "--role") == 0) {
101 // "-r" or "--role" has been specified
102 foundArgRole = true;
103 if (argv[a + 1][0] - 48 <= 1) {
104 role = (argv[a + 1][0] - 48) == 1;
105 }
106 else {
107 // option is invalid
108 invalidOption = true;
109 }
110 }
111 if (invalidOption) {
112 printHelp(string(argv[0]));
113 return 0;
114 }
115 a += 2;
116 } // while
117 if (!foundArgNode && !foundArgRole) {
118 // no valid args were specified
119 printHelp(string(argv[0]));
120 return 0;
121 }
122 } // else
123 } // if
124
125 // print example's name
126 cout << argv[0] << endl;
127
128 if (!foundArgNode) {
129 // Set the radioNumber via the terminal on startup
130 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
131 string input;
132 getline(cin, input);
133 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
134 }
135
136 // save on transmission time by setting the radio to only transmit the
137 // number of bytes we need to transmit a float
138 radio.setPayloadLength(SIZE); // default value is the maximum 32 bytes
139
140 // Set the PA Level low to try preventing power supply related problems
141 // because these examples are likely run with nodes in close proximity to
142 // each other.
143 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
144
145 // set the TX address of the RX node into the TX pipe
146 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
147
148 // set the RX address of the TX node into a RX pipe
149 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
150
151 // For debugging info
152 // radio.printDetails();
153
154 // ready to execute program now
155 if (!foundArgRole) { // if CLI arg "-r"/"--role" was not specified
156 setRole(); // calls master() or slave() based on user input
157 }
158 else { // if CLI arg "-r"/"--role" was specified
159 role ? master() : slave(); // based on CLI arg option
160 }
161 return 0;
162}
163
164
165/**
166 * set this node's role from stdin stream.
167 * this only considers the first char as input.
168 */
169void setRole() {
170 string input = "";
171 while (!input.length()) {
172 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
173 cout << "*** PRESS 'R' to begin receiving from the other node\n";
174 cout << "*** PRESS 'Q' to exit" << endl;
175 getline(cin, input);
176 if (input.length() >= 1) {
177 if (input[0] == 'T' || input[0] == 't')
178 master();
179 else if (input[0] == 'R' || input[0] == 'r')
180 slave();
181 else if (input[0] == 'Q' || input[0] == 'q')
182 break;
183 else
184 cout << input[0] << " is an invalid input. Please try again." << endl;
185 }
186 input = ""; // stay in the while loop
187 } // while
188} // setRole()
189
190
191/**
192 * make this node act as the transmitter
193 */
194void master() {
195 radio.stopListening(); // put radio in TX mode
196 radio.flushTx();
197 unsigned int failures = 0; // keep track of failures
198 uint8_t i = 0;
199 makePayload(i);
200 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
201 while (i < SIZE && failures < 100) {
202 while (!radio.write(&buffer, SIZE, false, false) && i < SIZE) {
203 i++;
204 makePayload(i);
205 }
206 radio.ce(true);
207 if (!radio.isFifo(true, true)) {
208 if (radio.irqDf()) {
209 failures++;
210 radio.ce(false);
211 radio.clearStatusFlags();
212 radio.ce(true);
213 }
214 if (failures >= 100) {
215 // most likely no device is listening for the data stream
216 cout << "Too many failures detected. ";
217 cout << "Aborting at payload " << buffer[0];
218 break;
219 }
220 }
221 } // while
222 radio.ce(false);
223 uint32_t ellapsedTime = getMicros(); // end the timer
224 cout << "Time to transmit data = ";
225 cout << ellapsedTime; // print the timer result
226 cout << " us. " << failures; // print number of retries
227 cout << " failures detected. Leaving TX role." << endl;
228} // master
229
230
231/**
232 * make this node act as the receiver
233 */
234void slave() {
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
253
254/**
255 * Make a single payload based on position in stream.
256 * This example employs this function to save on memory allocated.
257 */
258void makePayload(uint8_t i) {
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
270
271/**
272 * Calculate the ellapsed time in microseconds
273 */
274uint32_t getMicros() {
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
285
286/**
287 * print a manual page of instructions on how to use this example's CLI args
288 */
289void printHelp(string progName) {
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}
interruptConfigure.cpp¶
This is a simple example of using the RF24 class on a Raspberry Pi to detecting (and verifying) the IRQ (interrupt) pin on the nRF24L01.
This example was written to be used on 2 devices acting as “nodes”.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
16#include <ctime> // time()
17#include <iostream> // cin, cout, endl
18#include <string> // string, getline()
19#include <time.h> // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
20#include <RF24Revamped/RF24Revamped.h> // RF24, RF24_PA_LOW, delay(), pinMode(), INPUT, attachInterrupt(), INT_EDGE_FALLING
21
22using namespace std;
23
24// We will be using the nRF24L01's IRQ pin for this example
25#define IRQ_PIN 12 // this needs to be a digital input capable pin
26
27// this example is a sequential program. so we need to wait for the event to be handled
28volatile bool wait_for_event = false; // used to signify that the event is handled
29
30/****************** Linux ***********************/
31// Radio CE Pin, CSN Pin, SPI Speed
32// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
33// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
34// ie: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
35
36// Generic:
37RF24Revamped radio(22, 0);
38/****************** Linux (BBB,x86,etc) ***********************/
39// See http://2bndy5.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 a payload containing
44// a string that changes on every transmission. (successful or not)
45// Make a couple arrays of payloads & an iterator to traverse them
46const uint8_t tx_pl_size = 5;
47const uint8_t ack_pl_size = 4;
48uint8_t pl_iterator = 0;
49// The " + 1" compensates for the c-string's NULL terminating 0
50char tx_payloads[4][tx_pl_size + 1] = {"Ping ", "Pong ", "Radio", "1FAIL"};
51char ack_payloads[3][ack_pl_size + 1] = {"Yak ", "Back", " ACK"};
52
53void interruptHandler(); // prototype to handle the interrupt request (IRQ) pin
54void setRole(); // prototype to set the node's role
55void master(); // prototype of the TX node's behavior
56void slave(); // prototype of the RX node's behavior
57void ping_n_wait(); // prototype that sends a payload and waits for the IRQ pin to get triggered
58void printRxFifo(const uint8_t); // prototype to print entire contents of RX FIFO with 1 buffer
59
60
61int main(int argc, char** argv) {
62
63 // perform hardware check
64 if (!radio.begin()) {
65 cout << "radio hardware is not responding!!" << endl;
66 return 0; // quit now
67 }
68
69 // Let these addresses be used for the pair
70 uint8_t address[2][6] = {"1Node", "2Node"};
71 // It is very helpful to think of an address as a path instead of as
72 // an identifying device destination
73
74 // to use different addresses on a pair of radios, we need a variable to
75 // uniquely identify which address this radio will use to transmit
76 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
77
78 // print example's name
79 cout << argv[0] << endl;
80
81 // Set the radioNumber via the terminal on startup
82 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
83 string input;
84 getline(cin, input);
85 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
86
87 // to use ACK payloads, we need to enable dynamic payload lengths
88 radio.setDynamicPayloads(true); // ACK payloads are dynamically sized
89
90 // Acknowledgement packets have no payloads by default. We need to enable
91 // this feature for all nodes (TX & RX) to use ACK payloads.
92 radio.enableAckPayload();
93
94 // Set the PA Level low to try preventing power supply related problems
95 // because these examples are likely run with nodes in close proximity to
96 // each other.
97 radio.setPaLevel(RF24_PA_LOW); // RF24_PA_MAX is default.
98
99 // set the TX address of the RX node into the TX pipe
100 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
101
102 // set the RX address of the TX node into a RX pipe
103 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
104
105 // For debugging info
106 // radio.printDetails();
107
108 // setup the digital input pin connected to the nRF24L01's IRQ pin
109 pinMode(IRQ_PIN, INPUT);
110
111 // register the interrupt request (IRQ) to call our
112 // Interrupt Service Routine (ISR) callback function interruptHandler()
113 attachInterrupt(IRQ_PIN, INT_EDGE_FALLING, &interruptHandler);
114 // IMPORTANT: do not call radio.available() before calling
115 // radio.clearStatusFlags() when the interruptHandler() is triggered by the
116 // IRQ pin FALLING event. According to the datasheet, the pipe information
117 // is unreliable during the IRQ pin FALLING transition.
118
119 // ready to execute program now
120 setRole(); // calls master() or slave() based on user input
121 return 0;
122} // main
123
124
125/**
126 * set this node's role from stdin stream.
127 * this only considers the first char as input.
128 */
129void setRole() {
130 string input = "";
131 while (!input.length()) {
132 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
133 cout << "*** PRESS 'R' to begin receiving from the other node\n";
134 cout << "*** PRESS 'Q' to exit" << endl;
135 getline(cin, input);
136 if (input.length() >= 1) {
137 if (input[0] == 'T' || input[0] == 't')
138 master();
139 else if (input[0] == 'R' || input[0] == 'r')
140 slave();
141 else if (input[0] == 'Q' || input[0] == 'q')
142 break;
143 else
144 cout << input[0] << " is an invalid input. Please try again." << endl;
145 }
146 input = ""; // stay in the while loop
147 } // while
148} // setRole
149
150
151/**
152 * act as the transmitter to show 3 different IRQ events by sending 4 payloads:
153 * 1. Successfully receive ACK payload first
154 * 2. Successfully transmit on second
155 * 3. Send a third payload to fill RX node's RX FIFO (supposedly making RX node unresponsive)
156 * 4. intentionally fail transmit on the fourth
157 */
158void master() {
159 pl_iterator = 0; // reset the iterator for the following tests done in master()
160
161 // Test the "data ready" event with the IRQ pin
162 cout << "\nConfiguring IRQ pin to ignore the 'data sent' event\n";
163 radio.interruptConfig(true, false, true); // args = dataReady, dataSent, dataFail
164 cout << " Pinging RX node for 'data ready' event...";
165 ping_n_wait(); // transmit a payload and detect the IRQ pin
166 pl_iterator++; // increment iterator for next test
167
168
169 // Test the "data sent" event with the IRQ pin
170 cout << "\nConfiguring IRQ pin to ignore the 'data ready' event\n";
171 radio.interruptConfig(false, true, true); // args = dataReady, dataSent, dataFail
172 cout << " Pinging RX node for 'data sent' event...";
173 radio.flushTx(); // flush payloads from any failed prior test
174 ping_n_wait(); // transmit a payload and detect the IRQ pin
175 pl_iterator++; // increment iterator for next test
176
177
178 // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
179 // send() uses virtual interrupt flags that work despite the masking of the IRQ pin
180 radio.interruptConfig(false, false, false); // disable IRQ masking for this step
181
182 cout << "\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected.\n";
183 // send() will call flushTx() before calling write()
184 if (radio.send(&tx_payloads[pl_iterator], tx_pl_size))
185 cout << "RX node's FIFO is full; it is not listening any more" << endl;
186 else {
187 cout << "Transmission failed or timed out. Continuing anyway." << endl;
188 radio.flushTx();
189 }
190 pl_iterator++; // increment iterator for next test
191
192
193 // test the "data fail" event with the IRQ pin
194 cout << "\nConfiguring IRQ pin to reflect all events\n";
195 radio.interruptConfig(); // all args default to true
196 cout << " Pinging inactive RX node for 'data fail' event...";
197 ping_n_wait(); // transmit a payload and detect the IRQ pin
198
199 // CE pin is still HIGH which consumes more power. Example is now idling so...
200 radio.stopListening(); // ensure CE pin is LOW
201 // stopListening() also calls flushTx() when ACK payloads are enabled
202
203 if (radio.available()) {
204 printRxFifo(ack_pl_size); // doing this will flush the RX FIFO
205 }
206} // master
207
208
209/**
210 * act as the receiver
211 */
212void slave() {
213
214 // let IRQ pin only trigger on "data_ready" event in RX mode
215 radio.interruptConfig(true, false, false); // args = dataReady, dataSent, dataFail
216
217 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
218 // transmissions on pipe 0.
219 radio.writeAck(1, &ack_payloads[0], ack_pl_size);
220 radio.writeAck(1, &ack_payloads[1], ack_pl_size);
221 radio.writeAck(1, &ack_payloads[2], ack_pl_size);
222
223 radio.startListening(); // put radio in RX mode
224 time_t startTimer = time(nullptr); // start a timer
225 while (time(nullptr) - startTimer < 6 && !radio.isFifo(false, true)) {
226 // use 6 second timeout & wait till RX FIFO is full
227 }
228 delay(100); // wait for ACK payload to finish transmitting
229 radio.stopListening(); // also discards unused ACK payloads
230
231 if (radio.available()) {
232 printRxFifo(tx_pl_size);
233 }
234 else {
235 cout << "Timeout was reached. Going back to setRole()" << endl;
236 }
237} // slave
238
239
240/**
241 * pings the receiver with a non-blocking startWrite(), then waits till
242 * the IRQ pin is triggered
243 */
244void ping_n_wait() {
245 // use the non-blocking call to write a payload and begin transmission
246 // the "false" argument means we are expecting an ACK packet response
247 radio.write(tx_payloads[pl_iterator], tx_pl_size, false);
248
249 wait_for_event = true;
250 while (wait_for_event) {
251 /*
252 * IRQ pin is LOW when activated. Otherwise it is always HIGH
253 * Wait in this empty loop until IRQ pin is activated.
254 *
255 * In this example, the "data fail" event is always configured to
256 * trigger the IRQ pin active. Because the auto-ACK feature is on by
257 * default, we don't need a timeout check to prevent an infinite loop.
258 */
259 }
260}
261
262
263/**
264 * when the IRQ pin goes active LOW, call this fuction print out why
265 */
266void interruptHandler() {
267 // print IRQ status and all masking flags' states
268
269 cout << "\tIRQ pin is actively LOW" << endl; // show that this function was called
270
271 radio.clearStatusFlags(); // get values for IRQ masks
272 // clearStatusFlags() clears the IRQ masks also. This is required for
273 // continued TX operations when a transmission fails.
274 // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
275
276 cout << "\tdata_sent: " << radio.irqDs(); // print "data sent" mask state
277 cout << ", data_fail: " << radio.irqDf(); // print "data fail" mask state
278 cout << ", data_ready: " << radio.irqDr() << endl; // print "data ready" mask state
279
280 if (radio.irqDf()) // if TX payload failed
281 radio.flushTx(); // clear all payloads from the TX FIFO
282
283 // print if test passed or failed. Unintentional fails mean the RX node was not listening.
284 if (pl_iterator == 0)
285 cout << " 'Data Ready' event test " << (radio.irqDr() ? "passed" : "failed") << endl;
286 else if (pl_iterator == 1)
287 cout << " 'Data Sent' event test " << (radio.irqDs() ? "passed" : "failed") << endl;
288 else if (pl_iterator == 3)
289 cout << " 'Data Fail' event test " << (radio.irqDf() ? "passed" : "failed") << endl;
290
291 wait_for_event = false; // ready to continue
292} // interruptHandler
293
294
295/**
296 * Print the entire RX FIFO with one buffer. This will also flush the RX FIFO.
297 * @param pl_size used to determine received payload size. Remember that the
298 * payload sizes are declared as tx_pl_size and ack_pl_size.
299 */
300void printRxFifo(const uint8_t pl_size) {
301 char rx_fifo[pl_size * 3 + 1]; // assuming RX FIFO is full; declare a buffer to hold it all
302 if (radio.isFifo(false, true)) {
303 rx_fifo[pl_size * 3] = 0; // add a NULL terminating char to use as a c-string
304 radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
305 }
306 else {
307 uint8_t i = 0;
308 while (radio.available()) {
309 radio.read(&rx_fifo + (i * pl_size), pl_size);
310 i++;
311 }
312 rx_fifo[i * pl_size] = 0; // add a NULL terminating char to use as a c-string
313 }
314
315 // print the entire RX FIFO with 1 buffer
316 cout << "Complete RX FIFO: " << rx_fifo << endl;
317}
scanner.cpp¶
This example is best used as a diagnostic tool to determine what RF channels have less interference in your vicinity.
Use ctrl+c
to quit at any time, but remember state of the radio will persist until another example or application uses it.
25#include <cstdlib>
26#include <iostream>
27#include <RF24Revamped/RF24Revamped.h>
28
29using namespace std;
30
31/****************** Linux ***********************/
32// Radio CE Pin, CSN Pin, SPI Speed
33// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
34// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
35// ie: RF24Revamped radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
36
37// Generic:
38RF24Revamped radio(22, 0);
39/****************** Linux (BBB,x86,etc) ***********************/
40// See http://nRF24.github.io/RF24/pages.html for more information on usage
41// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
42// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
43
44// Channel info
45const uint8_t num_channels = 126;
46uint8_t values[num_channels];
47
48const int num_reps = 100;
49int reset_array = 0;
50
51int main(int argc, char** argv)
52{
53 // Print preamble
54
55 // print example's name
56 printf("%s", argv[0]);
57
58 //
59 // Setup and configure rf radio
60 //
61 radio.begin();
62
63 radio.setAutoAck(false);
64
65 // Get into standby mode
66 radio.startListening();
67 radio.stopListening();
68
69 radio.printDetails();
70
71 // Print out header, high then low digit
72 int i = 0;
73
74 while (i < num_channels) {
75 printf("%x", i >> 4);
76 ++i;
77 }
78 printf("\n");
79
80 i = 0;
81 while (i < num_channels) {
82 printf("%x", i & 0xf);
83 ++i;
84 }
85 printf("\n");
86
87 // forever loop
88 while (1) {
89 // Clear measurement values
90 memset(values, 0, sizeof(values));
91
92 // Scan all channels num_reps times
93 int rep_counter = num_reps;
94 while (rep_counter--) {
95
96 int i = num_channels;
97 while (i--) {
98
99 // Select this channel
100 radio.setChannel(i);
101
102 // Listen for a little
103 radio.startListening();
104 delayMicroseconds(128);
105 radio.stopListening();
106
107 // Did we get a carrier?
108 if (radio.testRpd()) {
109 ++values[i];
110 }
111 }
112 }
113
114 // Print out channel measurements, clamped to a single hex digit
115 i = 0;
116 while (i < num_channels) {
117 if (values[i])
118 printf("%x", min(0xf, (values[i] & 0xf)));
119 else
120 printf("-");
121
122 ++i;
123 }
124 printf("\n");
125 }
126
127 return 0;
128}
129
130// vim:ai:cin:sts=2 sw=2 ft=cpp