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