Optimized high speed nRF24L01+ driver class documentation v1.4.8
TMRh20 2020 - Optimized fork of the nRF24L01+ driver
Loading...
Searching...
No Matches
examples/InterruptConfigure/InterruptConfigure.ino

Written by 2bndy5 in 2020

This example uses Acknowledgement (ACK) payloads attached to ACK packets to demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be configured to detect when data is received, or when data has transmitted successfully, or when data has failed to transmit.

This example was written to be used on 2 devices acting as "nodes". Use the Serial Monitor to change each node's behavior.

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 <SPI.h>
17#include "printf.h"
18#include "RF24.h"
19
20// We will be using the nRF24L01's IRQ pin for this example
21#define IRQ_PIN 2 // this needs to be a digital input capable pin
22volatile bool wait_for_event = false; // used to wait for an IRQ event to trigger
23
24#define CE_PIN 7
25#define CSN_PIN 8
26// instantiate an object for the nRF24L01 transceiver
27RF24 radio(CE_PIN, CSN_PIN);
28
29// Let these addresses be used for the pair
30uint8_t address[][6] = { "1Node", "2Node" };
31// It is very helpful to think of an address as a path instead of as
32// an identifying device destination
33
34// to use different addresses on a pair of radios, we need a variable to
35// uniquely identify which address this radio will use to transmit
36bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
37
38// Used to control whether this node is sending or receiving
39bool role = false; // true = TX node, false = RX node
40
41// For this example, we'll be using a payload containing
42// a string that changes on every transmission. (successful or not)
43// Make a couple arrays of payloads & an iterator to traverse them
44const uint8_t tx_pl_size = 5;
45const uint8_t ack_pl_size = 4;
46uint8_t pl_iterator = 0;
47// The " + 1" compensates for the c-string's NULL terminating 0
48char tx_payloads[][tx_pl_size + 1] = { "Ping ", "Pong ", "Radio", "1FAIL" };
49char ack_payloads[][ack_pl_size + 1] = { "Yak ", "Back", " ACK" };
50
51void interruptHandler(); // prototype to handle IRQ events
52void printRxFifo(); // prototype to print RX FIFO with 1 buffer
53
54
55void setup() {
56 Serial.begin(115200);
57 while (!Serial) {
58 // some boards need to wait to ensure access to serial over USB
59 }
60
61 // initialize the transceiver on the SPI bus
62 if (!radio.begin()) {
63 Serial.println(F("radio hardware is not responding!!"));
64 while (1) {} // hold in infinite loop
65 }
66
67 // print example's introductory prompt
68 Serial.println(F("RF24/examples/InterruptConfigure"));
69
70 // To set the radioNumber via the Serial monitor on startup
71 Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
72 while (!Serial.available()) {
73 // wait for user input
74 }
75 char input = Serial.parseInt();
76 radioNumber = input == 1;
77 Serial.print(F("radioNumber = "));
78 Serial.println((int)radioNumber);
79
80 // role variable is hardcoded to RX behavior, inform the user of this
81 Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
82
83 // setup the IRQ_PIN
84 pinMode(IRQ_PIN, INPUT);
85 attachInterrupt(digitalPinToInterrupt(IRQ_PIN), interruptHandler, FALLING);
86 // IMPORTANT: do not call radio.available() before calling
87 // radio.whatHappened() when the interruptHandler() is triggered by the
88 // IRQ pin FALLING event. According to the datasheet, the pipe information
89 // is unreliable during the IRQ pin FALLING transition.
90
91 // Set the PA Level low to try preventing power supply related problems
92 // because these examples are likely run with nodes in close proximity to
93 // each other.
94 radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
95
96 // For this example we use acknowledgment (ACK) payloads to trigger the
97 // IRQ pin when data is received on the TX node.
98 // to use ACK payloads, we need to enable dynamic payload lengths
99 radio.enableDynamicPayloads(); // ACK payloads are dynamically sized
100
101 // Acknowledgement packets have no payloads by default. We need to enable
102 // this feature for all nodes (TX & RX) to use ACK payloads.
103 radio.enableAckPayload();
104 // Fot this example, we use the same address to send data back and forth
105
106 // set the TX address of the RX node into the TX pipe
107 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
108
109 // set the RX address of the TX node into a RX pipe
110 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
111
112 // additional setup specific to the node's role
113 if (role) {
114 // setup for TX mode
115 radio.stopListening(); // put radio in TX mode
116
117 } else {
118 // setup for RX mode
119
120 // let IRQ pin only trigger on "data ready" event in RX mode
121 radio.maskIRQ(1, 1, 0); // args = "data_sent", "data_fail", "data_ready"
122
123 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
124 // transmissions on pipe 1
125 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
126 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
127 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
128
129 radio.startListening(); // put radio in RX mode
130 }
131
132 // For debugging info
133 // printf_begin(); // needed only once for printing details
134 // radio.printDetails(); // (smaller) function that prints raw register values
135 // radio.printPrettyDetails(); // (larger) function that prints human readable data
136}
137
138void loop() {
139 if (role && !wait_for_event) {
140
141 // delay(1); // wait for IRQ pin to fully RISE
142
143 // This device is a TX node. This if block is only triggered when
144 // NOT waiting for an IRQ event to happen
145
146 if (pl_iterator == 0) {
147 // Test the "data ready" event with the IRQ pin
148
149 Serial.println(F("\nConfiguring IRQ pin to ignore the 'data sent' event"));
150 radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
151 Serial.println(F(" Pinging RX node for 'data ready' event..."));
152
153 } else if (pl_iterator == 1) {
154 // Test the "data sent" event with the IRQ pin
155
156 Serial.println(F("\nConfiguring IRQ pin to ignore the 'data ready' event"));
157 radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
158 Serial.println(F(" Pinging RX node for 'data sent' event..."));
159
160 } else if (pl_iterator == 2) {
161 // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
162
163 // write() uses virtual interrupt flags that work despite the masking of the IRQ pin
164 radio.maskIRQ(1, 1, 1); // disable IRQ masking for this step
165
166 Serial.println(F("\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected."));
167 // write() will call flush_tx() on 'data fail' events
168 if (radio.write(&tx_payloads[pl_iterator], tx_pl_size)) {
169 if (radio.rxFifoFull()) {
170 Serial.println(F("RX node's FIFO is full; it is not listening any more"));
171 } else {
172 Serial.println("Transmission successful, but the RX node might still be listening.");
173 }
174 } else {
175 Serial.println(F("Transmission failed or timed out. Continuing anyway."));
176 radio.flush_tx(); // discard payload(s) that failed to transmit
177 }
178
179 } else if (pl_iterator == 3) {
180 // test the "data fail" event with the IRQ pin
181
182 Serial.println(F("\nConfiguring IRQ pin to reflect all events"));
183 radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
184 Serial.println(F(" Pinging inactive RX node for 'data fail' event..."));
185 }
186
187 if (pl_iterator < 4 && pl_iterator != 2) {
188
189 // IRQ pin is LOW when activated. Otherwise it is always HIGH
190 // Wait until IRQ pin is activated.
191 wait_for_event = true;
192
193 // use the non-blocking call to write a payload and begin transmission
194 // the "false" argument means we are expecting an ACK packet response
195 radio.startFastWrite(tx_payloads[pl_iterator++], tx_pl_size, false);
196
197 // In this example, the "data fail" event is always configured to
198 // trigger the IRQ pin active. Because the auto-ACK feature is on by
199 // default, we don't need a timeout check to prevent an infinite loop.
200
201 } else if (pl_iterator == 4) {
202 // all IRQ tests are done; flush_tx() and print the ACK payloads for fun
203
204 // CE pin is still HIGH which consumes more power. Example is now idling so...
205 radio.stopListening(); // ensure CE pin is LOW
206 // stopListening() also calls flush_tx() when ACK payloads are enabled
207
208 printRxFifo();
209 pl_iterator++;
210
211
212 // inform user what to do next
213 Serial.println(F("\n*** PRESS 'T' to restart the transmissions"));
214 Serial.println(F("*** PRESS 'R' to change to Receive role\n"));
215
216
217 } else if (pl_iterator == 2) {
218 pl_iterator++; // proceed from step 3 to last step (stop at step 4 for readability)
219 }
220
221 } else if (!role) {
222 // This device is a RX node
223
224 if (radio.rxFifoFull()) {
225 // wait until RX FIFO is full then stop listening
226
227 delay(100); // let ACK payload finish transmitting
228 radio.stopListening(); // also discards unused ACK payloads
229 printRxFifo(); // flush the RX FIFO
230
231 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
232 // transmissions on pipe 1.
233 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
234 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
235 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
236
237 delay(100); // let TX node finish its role
238 radio.startListening(); // We're ready to start over. Begin listening.
239 }
240
241 } // role
242
243 if (Serial.available()) {
244 // change the role via the serial monitor
245
246 char c = toupper(Serial.read());
247 if (c == 'T') {
248 // Become the TX node
249 if (!role)
250 Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
251 else
252 Serial.println(F("*** RESTARTING IRQ PIN TEST ***"));
253
254 role = true;
255 wait_for_event = false;
256 pl_iterator = 0; // reset the iterator
257 radio.flush_tx(); // discard any payloads in the TX FIFO
258
259 // startListening() clears the IRQ masks also. This is required for
260 // continued TX operations when a transmission fails.
261 radio.stopListening(); // this also discards any unused ACK payloads
262
263 } else if (c == 'R' && role) {
264 // Become the RX node
265 Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
266
267 role = false;
268
269 radio.maskIRQ(0, 0, 0); // the IRQ pin should only trigger on "data ready" event
270
271 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
272 // transmissions on pipe 1
273 radio.flush_tx(); // make sure there is room for 3 new ACK payloads
274 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
275 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
276 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
277 radio.startListening();
278 }
279 } // Serial.available()
280} // loop
281
282
286void interruptHandler() {
287 // print IRQ status and all masking flags' states
288
289 Serial.println(F("\tIRQ pin is actively LOW")); // show that this function was called
291 bool tx_ds, tx_df, rx_dr; // declare variables for IRQ masks
292 radio.whatHappened(tx_ds, tx_df, rx_dr); // get values for IRQ masks
293 // whatHappened() clears the IRQ masks also. This is required for
294 // continued TX operations when a transmission fails.
295 // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
296
297 Serial.print(F("\tdata_sent: "));
298 Serial.print(tx_ds); // print "data sent" mask state
299 Serial.print(F(", data_fail: "));
300 Serial.print(tx_df); // print "data fail" mask state
301 Serial.print(F(", data_ready: "));
302 Serial.println(rx_dr); // print "data ready" mask state
303
304 if (tx_df) // if TX payload failed
305 radio.flush_tx(); // clear all payloads from the TX FIFO
306
307 // print if test passed or failed. Unintentional fails mean the RX node was not listening.
308 // pl_iterator has already been incremented by now
309 if (pl_iterator <= 1) {
310 Serial.print(F(" 'Data Ready' event test "));
311 Serial.println(rx_dr ? F("passed") : F("failed"));
312 } else if (pl_iterator == 2) {
313 Serial.print(F(" 'Data Sent' event test "));
314 Serial.println(tx_ds ? F("passed") : F("failed"));
315 } else if (pl_iterator == 4) {
316 Serial.print(F(" 'Data Fail' event test "));
317 Serial.println(tx_df ? F("passed") : F("failed"));
318 }
319 wait_for_event = false; // ready to continue with loop() operations
320} // interruptHandler
321
322
327void printRxFifo() {
328 if (radio.available()) { // if there is data in the RX FIFO
329 // to flush the data from the RX FIFO, we'll fetch it all using 1 buffer
330
331 uint8_t pl_size = !role ? tx_pl_size : ack_pl_size;
332 char rx_fifo[pl_size * 3 + 1]; // RX FIFO is full & we know ACK payloads' size
333 if (radio.rxFifoFull()) {
334 rx_fifo[pl_size * 3] = 0; // add a NULL terminating char to use as a c-string
335 radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
336 } else {
337 uint8_t i = 0;
338 while (radio.available()) {
339 radio.read(&rx_fifo + (i * pl_size), pl_size);
340 i++;
341 }
342 rx_fifo[i * pl_size] = 0; // add a NULL terminating char to use as a c-string
343 }
344 Serial.print(F("Complete RX FIFO: "));
345 Serial.println(rx_fifo); // print the entire RX FIFO with 1 buffer
346 }
347}
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition: RF24.h:116
@ RF24_PA_LOW
Definition: RF24.h:50
#define INPUT
#define pinMode(pin, direction)
#define delay(milisec)
#define delayMicroseconds(usec)