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 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 // Fot this example, we use the same address to send data back and forth
106
107 // set the TX address of the RX node into the TX pipe
108 radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
109
110 // set the RX address of the TX node into a RX pipe
111 radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
112
113 // additional setup specific to the node's role
114 if (role) {
115 // setup for TX mode
116 radio.stopListening(); // put radio in TX mode
117
118 } else {
119 // setup for RX mode
120
121 // let IRQ pin only trigger on "data ready" event in RX mode
122 radio.maskIRQ(1, 1, 0); // args = "data_sent", "data_fail", "data_ready"
123
124 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
125 // transmissions on pipe 1
126 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
127 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
128 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
129
130 radio.startListening(); // put radio in RX mode
131 }
132
133 // For debugging info
134 // printf_begin(); // needed only once for printing details
135 // radio.printDetails(); // (smaller) function that prints raw register values
136 // radio.printPrettyDetails(); // (larger) function that prints human readable data
137}
138
139void loop() {
140 if (got_interrupt) {
141 assessInterruptEvent();
142 }
143
144 if (role && !wait_for_event) {
145
146 // This device is a TX node. This if block is only triggered when
147 // NOT waiting for an IRQ event to happen
148
149 if (pl_iterator == 0) {
150 // Test the "data ready" event with the IRQ pin
151
152 Serial.println(F("\nConfiguring IRQ pin to ignore the 'data sent' event"));
153 radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
154 Serial.println(F(" Pinging RX node for 'data ready' event..."));
155
156 } else if (pl_iterator == 1) {
157 // Test the "data sent" event with the IRQ pin
158
159 Serial.println(F("\nConfiguring IRQ pin to ignore the 'data ready' event"));
160 radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
161 Serial.println(F(" Pinging RX node for 'data sent' event..."));
162
163 } else if (pl_iterator == 2) {
164 // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
165
166 // write() uses virtual interrupt flags that work despite the masking of the IRQ pin
167 radio.maskIRQ(1, 1, 1); // disable IRQ masking for this step
168
169 Serial.println(F("\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected."));
170 // write() will call flush_tx() on 'data fail' events
171 if (radio.write(&tx_payloads[pl_iterator], tx_pl_size)) {
172 if (radio.rxFifoFull()) {
173 Serial.println(F("RX node's FIFO is full; it is not listening any more"));
174 } else {
175 Serial.println("Transmission successful, but the RX node might still be listening.");
176 }
177 } else {
178 Serial.println(F("Transmission failed or timed out. Continuing anyway."));
179 radio.flush_tx(); // discard payload(s) that failed to transmit
180 }
181
182 } else if (pl_iterator == 3) {
183 // test the "data fail" event with the IRQ pin
184
185 Serial.println(F("\nConfiguring IRQ pin to reflect all events"));
186 radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
187 Serial.println(F(" Pinging inactive RX node for 'data fail' event..."));
188 }
189
190 if (pl_iterator < 4 && pl_iterator != 2) {
191
192 // IRQ pin is LOW when activated. Otherwise it is always HIGH
193 // Wait until IRQ pin is activated.
194 wait_for_event = true;
195
196 // use the non-blocking call to write a payload and begin transmission
197 // the "false" argument means we are expecting an ACK packet response
198 radio.startFastWrite(tx_payloads[pl_iterator++], tx_pl_size, false);
199
200 // In this example, the "data fail" event is always configured to
201 // trigger the IRQ pin active. Because the auto-ACK feature is on by
202 // default, we don't need a timeout check to prevent an infinite loop.
203
204 } else if (pl_iterator == 4) {
205 // all IRQ tests are done; flush_tx() and print the ACK payloads for fun
206
207 // CE pin is still HIGH which consumes more power. Example is now idling so...
208 radio.stopListening(); // ensure CE pin is LOW
209 // stopListening() also calls flush_tx() when ACK payloads are enabled
210
211 printRxFifo();
212 pl_iterator++;
213
214
215 // inform user what to do next
216 Serial.println(F("\n*** PRESS 'T' to restart the transmissions"));
217 Serial.println(F("*** PRESS 'R' to change to Receive role\n"));
218
219
220 } else if (pl_iterator == 2) {
221 pl_iterator++; // proceed from step 3 to last step (stop at step 4 for readability)
222 }
223
224 } else if (!role && radio.rxFifoFull()) {
225 // This device is a RX node:
226 // wait until RX FIFO is full then stop listening
227
228 delay(100); // let ACK payload finish transmitting
229 radio.stopListening(); // also discards unused ACK payloads
230 printRxFifo(); // flush the RX FIFO
231
232 // Fill the TX FIFO with 3 ACK payloads for the first 3 received
233 // transmissions on pipe 1.
234 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
235 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
236 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
237
238 delay(100); // let TX node finish its role
239 radio.startListening(); // We're ready to start over. Begin listening.
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.flush_rx(); // make sure there is room for 3 incoming payloads
275 radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
276 radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
277 radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
278 radio.startListening();
279 }
280 } // Serial.available()
281} // loop
282
283
288void interruptHandler() {
289 got_interrupt = true; // forward event handling back to main loop()
290}
291
296void assessInterruptEvent() {
297 // print IRQ status and all masking flags' states
298
299 Serial.println(F("\tIRQ pin is actively LOW")); // show that this function was called
301 bool tx_ds, tx_df, rx_dr; // declare variables for IRQ masks
302 radio.whatHappened(tx_ds, tx_df, rx_dr); // get values for IRQ masks
303 // whatHappened() clears the IRQ masks also. This is required for
304 // continued TX operations when a transmission fails.
305 // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
306
307 Serial.print(F("\tdata_sent: "));
308 Serial.print(tx_ds); // print "data sent" mask state
309 Serial.print(F(", data_fail: "));
310 Serial.print(tx_df); // print "data fail" mask state
311 Serial.print(F(", data_ready: "));
312 Serial.println(rx_dr); // print "data ready" mask state
313
314 if (tx_df) // if TX payload failed
315 radio.flush_tx(); // clear all payloads from the TX FIFO
316
317 // print if test passed or failed. Unintentional fails mean the RX node was not listening.
318 // pl_iterator has already been incremented by now
319 if (pl_iterator <= 1) {
320 Serial.print(F(" 'Data Ready' event test "));
321 Serial.println(rx_dr ? F("passed") : F("failed"));
322 } else if (pl_iterator == 2) {
323 Serial.print(F(" 'Data Sent' event test "));
324 Serial.println(tx_ds ? F("passed") : F("failed"));
325 } else if (pl_iterator == 4) {
326 Serial.print(F(" 'Data Fail' event test "));
327 Serial.println(tx_df ? F("passed") : F("failed"));
328 }
329 got_interrupt = false; // reset this flag to prevent calling this function from loop()
330 wait_for_event = false; // ready to continue with loop() operations
331}
332
337void printRxFifo() {
338 if (radio.available()) { // if there is data in the RX FIFO
339 // to flush the data from the RX FIFO, we'll fetch it all using 1 buffer
340
341 uint8_t pl_size = !role ? tx_pl_size : ack_pl_size;
342 char rx_fifo[pl_size * 3 + 1]; // RX FIFO is full & we know ACK payloads' size
343 if (radio.rxFifoFull()) {
344 rx_fifo[pl_size * 3] = 0; // add a NULL terminating char to use as a c-string
345 radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
346 } else {
347 uint8_t i = 0;
348 while (radio.available()) {
349 radio.read(&rx_fifo + (i * pl_size), pl_size);
350 i++;
351 }
352 rx_fifo[i * pl_size] = 0; // add a NULL terminating char to use as a c-string
353 }
354 Serial.print(F("Complete RX FIFO: "));
355 Serial.println(rx_fifo); // print the entire RX FIFO with 1 buffer
356 }
357}
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)