Krolyxon
Commit 1d55752

Branches

Tags

Refs →

orion-rf.piolibdepsesp32-s3-n16r8RF24examplesInterruptConfigureInterruptConfigure.ino

353 lines · 13.6 KB

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
7/**
8 * This example uses Acknowledgement (ACK) payloads attached to ACK packets to
9 * demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
10 * configured to detect when data is received, or when data has transmitted
11 * successfully, or when data has failed to transmit.
12 *
13 * This example was written to be used on 2 devices acting as "nodes".
14 * Use the Serial Monitor to change each node's behavior.
15 */
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
281/**
282 * when the IRQ pin goes active LOW.
283 * Here we just tell the main loop() to call `assessInterruptEve4nt()`.
284 */
285void interruptHandler() {
286 got_interrupt = true; // forward event handling back to main loop()
287}
288
289/**
290 * Called when an event has been triggered.
291 * Here, we want to verify the expected IRQ flag has been asserted.
292 */
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
297 delayMicroseconds(250);
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
329/**
330 * Print the entire RX FIFO with one buffer. This will also flush the RX FIFO.
331 * Remember that the payload sizes are declared as tx_pl_size and ack_pl_size.
332 */
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}