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

Written by 2bndy5 in 2020

A simple example of sending data from 1 nRF24L01 transceiver to another with manually transmitted (non-automatic) Acknowledgement (ACK) payloads. This example still uses ACK packets, but they have no payloads. Instead the acknowledging response is sent with write(). This tactic allows for more updated acknowledgement payload data, where actual ACK payloads' data are outdated by 1 transmission because they have to loaded before receiving a transmission.

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
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 <RF24/RF24.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: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
32#define CSN_PIN 0
33#ifdef MRAA
34 #define CE_PIN 15 // 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 http://iotdk.intel.com/docs/master/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 a payload containing
46// a string & an integer number that will be incremented
47// on every successful transmission.
48// Make a data structure to store the entire payload of different datatypes
49struct PayloadStruct
50{
51 char message[7]; // only using 6 characters for TX & RX payloads
52 uint8_t counter;
53};
54PayloadStruct payload;
55
56void setRole(); // prototype to set the node's role
57void master(); // prototype of the TX node's behavior
58void slave(); // prototype of the RX node's behavior
59
60// custom defined timer for evaluating transmission time in microseconds
61struct timespec startTimer, endTimer;
62uint32_t getMicros(); // prototype to get elapsed time in microseconds
63
64int main(int argc, char** argv)
65{
66
67 // perform hardware check
68 if (!radio.begin()) {
69 cout << "radio hardware is not responding!!" << endl;
70 return 0; // quit now
71 }
72
73 // append a NULL terminating 0 for printing as a c-string
74 payload.message[6] = 0;
75
76 // Let these addresses be used for the pair of nodes used in this example
77 uint8_t address[2][6] = {"1Node", "2Node"};
78 // the TX address^ , ^the RX address
79 // It is very helpful to think of an address as a path instead of as
80 // an identifying device destination
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 bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
85
86 // print example's name
87 cout << argv[0] << endl;
88
89 // Set the radioNumber via the terminal on startup
90 cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
91 string input;
92 getline(cin, input);
93 radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
94
95 // Set the PA Level low to try preventing power supply related problems
96 // because these examples are likely run with nodes in close proximity to
97 // each other.
98 radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
99
100 // save on transmission time by setting the radio to only transmit the
101 // number of bytes we need to transmit a float
102 radio.setPayloadSize(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
103
104 // set the TX address of the RX node into the TX pipe
105 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
106
107 // set the RX address of the TX node into a RX pipe
108 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
109
110 // For debugging info
111 // radio.printDetails(); // (smaller) function that prints raw register values
112 // radio.printPrettyDetails(); // (larger) function that prints human readable data
113
114 // ready to execute program now
115 setRole(); // calls master() or slave() based on user input
116 return 0;
117} // main
118
123void setRole()
124{
125 string input = "";
126 while (!input.length()) {
127 cout << "*** PRESS 'T' to begin transmitting to the other node\n";
128 cout << "*** PRESS 'R' to begin receiving from the other node\n";
129 cout << "*** PRESS 'Q' to exit" << endl;
130 getline(cin, input);
131 if (input.length() >= 1) {
132 if (input[0] == 'T' || input[0] == 't')
133 master();
134 else if (input[0] == 'R' || input[0] == 'r')
135 slave();
136 else if (input[0] == 'Q' || input[0] == 'q')
137 break;
138 else
139 cout << input[0] << " is an invalid input. Please try again." << endl;
140 }
141 input = ""; // stay in the while loop
142 } // while
143} // setRole()
144
148void master()
149{
150
151 memcpy(payload.message, "Hello ", 6); // set the outgoing message
152 radio.stopListening(); // put in TX mode
153
154 unsigned int failures = 0; // keep track of failures
155 while (failures < 6) {
156 clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
157 bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
158
159 if (report) {
160 // transmission successful; wait for response and print results
161
162 radio.startListening(); // put in RX mode
163 unsigned long start_timeout = millis(); // timer to detect no response
164 while (!radio.available()) { // wait for response
165 if (millis() - start_timeout > 200) // only wait 200 ms
166 break;
167 }
168 unsigned long elapsedTime = getMicros(); // end the timer
169 radio.stopListening(); // put back in TX mode
170
171 // print summary of transactions
172 uint8_t pipe;
173 cout << "Transmission successful! ";
174 if (radio.available(&pipe)) { // is there a payload received? grab the pipe number that received it
175 uint8_t bytes = radio.getPayloadSize(); // grab the incoming payload size
176 cout << "Round trip delay = ";
177 cout << elapsedTime; // print the timer result
178 cout << " us. Sent: " << payload.message; // print outgoing message
179 cout << (unsigned int)payload.counter; // print outgoing counter
180 PayloadStruct received;
181 radio.read(&received, sizeof(received)); // get incoming payload
182 cout << " Recieved " << (unsigned int)bytes; // print incoming payload size
183 cout << " on pipe " << (unsigned int)pipe; // print RX pipe number
184 cout << ": " << received.message; // print the incoming message
185 cout << (unsigned int)received.counter; // print the incoming counter
186 cout << endl;
187 payload.counter = received.counter; // save incoming counter for next outgoing counter
188 }
189 else {
190 cout << "Recieved no response." << endl; // no response received
191 }
192 }
193 else {
194 cout << "Transmission failed or timed out"; // payload was not delivered
195 cout << endl;
196 failures++; // increment failure counter
197 } // report
198
199 // to make this example readable in the terminal
200 delay(1000); // slow transmissions down by 1 second
201 } // while
202
203 cout << failures << " failures detected. Leaving TX role." << endl;
204} // master
205
209void slave()
210{
211 memcpy(payload.message, "World ", 6); // set the response message
212 radio.startListening(); // put in RX mode
213
214 time_t startTimer = time(nullptr); // start a timer
215 while (time(nullptr) - startTimer < 6) { // use 6 second timeout
216 uint8_t pipe;
217 if (radio.available(&pipe)) { // is there a payload? get the pipe number that recieved it
218 uint8_t bytes = radio.getPayloadSize(); // get size of incoming payload
219 PayloadStruct received;
220 radio.read(&received, sizeof(received)); // get incoming payload
221 payload.counter = received.counter + 1; // increment payload for response
222
223 // transmit response & save result to `report`
224 radio.stopListening(); // put in TX mode
225 radio.writeFast(&payload, sizeof(payload)); // load response into TX FIFO
226 bool report = radio.txStandBy(150); // keep retrying for 150 ms
227 radio.startListening(); // put back in RX mode
228
229 // print summary of transactions
230 cout << "Received " << (unsigned int)bytes; // print the size of the payload
231 cout << " bytes on pipe ";
232 cout << (unsigned int)pipe; // print the pipe number
233 cout << ": " << received.message; // print incoming message
234 cout << (unsigned int)received.counter; // print incoming counter
235
236 if (report) {
237 cout << " Sent: " << payload.message; // print outgoing message
238 cout << (unsigned int)payload.counter; // print outgoing counter
239 cout << endl;
240 }
241 else {
242 cout << " Response failed to send." << endl; // failed to send response
243 }
244 startTimer = time(nullptr); // reset timer
245 } // available
246 } // while
247
248 cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
249 radio.stopListening(); // recommended idle mode is TX mode
250} // slave
251
255uint32_t getMicros()
256{
257 // this function assumes that the timer was started using
258 // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
259
260 clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
261 uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
262 uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
263
264 return ((seconds)*1000 + useconds) + 0.5;
265}
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition: RF24.h:116
@ RF24_PA_LOW
Definition: RF24.h:50
#define delay(milisec)
#define millis()