forked from nRF24/RF24Network
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RF24Network.h
557 lines (515 loc) · 24.2 KB
/
RF24Network.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/*
Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef __RF24NETWORK_H__
#define __RF24NETWORK_H__
/**
* @file RF24Network.h
*
* Class declaration for RF24Network
*/
#include <stddef.h>
#include <stdint.h>
class RF24;
/**
* Header which is sent with each message
*
* The frame put over the air consists of this header and a message
*/
struct RF24NetworkHeader
{
uint16_t from_node; /**< Logical address where the message was generated */
uint16_t to_node; /**< Logical address where the message is going */
uint16_t id; /**< Sequential message ID, incremented every message */
unsigned char type; /**< Type of the packet. 0-127 are user-defined types, 128-255 are reserved for system */
unsigned char reserved; /**< Reserved for future use */
static uint16_t next_id; /**< The message ID of the next message to be sent */
/**
* Default constructor
*
* Simply constructs a blank header
*/
RF24NetworkHeader() {}
/**
* Send constructor
*
* Use this constructor to create a header and then send a message
*
* @code
* RF24NetworkHeader header(recipient_address,'t');
* network.write(header,&message,sizeof(message));
* @endcode
*
* @param _to The logical node address where the message is going
* @param _type The type of message which follows. Only 0-127 are allowed for
* user messages.
*/
RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type&0x7f) {}
/**
* Create debugging string
*
* Useful for debugging. Dumps all members into a single string, using
* internal static memory. This memory will get overridden next time
* you call the method.
*
* @return String representation of this object
*/
const char* toString(void) const;
};
/**
* 2014 - Optimized Network Layer for RF24 Radios
*
* This class implements an OSI Network Layer using nRF24L01(+) radios driven
* by RF24 library.
*/
class RF24Network
{
public:
/**
* Construct the network
*
* @param _radio The underlying radio driver instance
*
*/
RF24Network( RF24& _radio );
/**
* Bring up the network
*
* @warning Be sure to 'begin' the radio first.
*
* @param _channel The RF channel to operate on
* @param _node_address The logical address of this node
*/
void begin(uint8_t _channel, uint16_t _node_address );
/**
* Main layer loop
*
* This function must be called regularly to keep the layer going. This is where all
* the action happens!
*/
void update(void);
/**
* Test whether there is a message available for this node
*
* @return Whether there is a message available for this node
*/
bool available(void);
/**
* Read the next available header
*
* Reads the next available header without advancing to the next
* incoming message. Useful for doing a switch on the message type
*
* If there is no message available, the header is not touched
*
* @param[out] header The header (envelope) of the next message
*/
void peek(RF24NetworkHeader& header);
/**
* Read a message
*
* @param[out] header The header (envelope) of this message
* @param[out] message Pointer to memory where the message should be placed
* @param maxlen The largest message size which can be held in @p message
* @return The total number of bytes copied into @p message
*/
size_t read(RF24NetworkHeader& header, void* message, size_t maxlen);
/**
* Send a message
*
* @note Optimization: Extended timeouts/retries enabled. See txTimeout for more info.
* @param[in,out] header The header (envelope) of this message. The critical
* thing to fill in is the @p to_node field so we know where to send the
* message. It is then updated with the details of the actual header sent.
* @param message Pointer to memory where the message is located
* @param len The size of the message
* @return Whether the message was successfully received
*/
bool write(RF24NetworkHeader& header,const void* message, size_t len);
/**
* Sleep this node - Still Under Development
* @note NEW - Nodes can now be slept while the radio is not actively transmitting. This must be manually enabled by uncommenting
* the #define ENABLE_SLEEP_MODE in RF24Network_config.h
* @note Setting the interruptPin to 255 will disable interrupt wake-ups
* @note The watchdog timer should be configured in setup() if using sleep mode.
* This function will sleep the node, with the radio still active in receive mode.
*
* The node can be awoken in two ways, both of which can be enabled simultaneously:
* 1. An interrupt - usually triggered by the radio receiving a payload. Must use pin 2 (interrupt 0) or 3 (interrupt 1) on Uno, Nano, etc.
* 2. The watchdog timer waking the MCU after a designated period of time, can also be used instead of delays to control transmission intervals.
* @code
* if(!network.available()){ network.sleepNode(1,0); } //Sleeps the node for 1 second or a payload is received
*
* Other options:
* network.sleepNode(0,0); // Sleep this node for the designated time period, or a payload is received.
* network.sleepNode(1,255); // Sleep this node for 1 cycle. Do not wake up until then, even if a payload is received ( no interrupt )
* @endcode
* @see setup_watchdog()
* @param cycles: The node will sleep in cycles of 1s. Using 2 will sleep 2 WDT cycles, 3 sleeps 3WDT cycles...
* @param interruptPin: The interrupt number to use (0,1) for pins two and three on Uno,Nano. More available on Mega etc.
*
*/
void sleepNode( unsigned int cycles, int interruptPin );
/**
* Set up the watchdog timer for sleep mode using the number 0 through 10 to represent the following time periods:<br>
* wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s
* @code
* setup_watchdog(7); // Sets the WDT to trigger every second
* @endcode
* @param prescalar The WDT prescaler to define how often the node will wake up. When defining sleep mode cycles, this time period is 1 cycle.
*/
void setup_watchdog(uint8_t prescalar);
/**
* This node's parent address
*
* @return This node's parent address, or -1 if this is the base
*/
uint16_t parent() const;
/**@}*/
/**
* @name Advanced Operation
*
* For advanced configuration of the network
*/
/**@{*/
/**
* Construct the network in dual head mode using two radio modules.
* @note Radios will share MISO, MOSI and SCK pins, but require separate CE,CS pins.
* @code
* RF24 radio(7,8);
* RF24 radio1(4,5);
* RF24Network(radio.radio1);
* @endcode
* @param _radio The underlying radio driver instance
* @param _radio1 The second underlying radio driver instance
*/
RF24Network( RF24& _radio, RF24& _radio1);
/**
* @note: Optimization:This value is automatically assigned based on the node address
* to reduce errors and increase throughput of the network.
*
* Sets the timeout period for individual payloads in milliseconds at staggered intervals.
* Payloads will be retried automatically until success or timeout
* Set to 0 to use the normal auto retry period defined by radio.setRetries()
*
*/
unsigned long txTimeout;
private:
void open_pipes(void);
uint16_t find_node( uint16_t current_node, uint16_t target_node );
bool write(uint16_t);
bool write_to_pipe( uint16_t node, uint8_t pipe );
bool enqueue(void);
bool is_direct_child( uint16_t node );
bool is_descendant( uint16_t node );
uint16_t direct_child_route_to( uint16_t node );
uint8_t pipe_to_descendant( uint16_t node );
void setup_address(void);
private:
RF24& radio; /**< Underlying radio driver, provides link/physical layers */
#if defined (DUAL_HEAD_RADIO)
RF24& radio1;
#endif
uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */
const static int frame_size = 32; /**< How large is each frame over the air */
uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */
uint8_t frame_queue[5*frame_size]; /**< Space for a small set of frames that need to be delivered to the app layer */
uint8_t* next_frame; /**< Pointer into the @p frame_queue where we should place the next received frame */
uint16_t parent_node; /**< Our parent's node address */
uint8_t parent_pipe; /**< The pipe our parent uses to listen to us */
uint16_t node_mask; /**< The bits which contain signfificant node address information */
};
/**
* @example helloworld_tx.ino
*
* Simplest possible example of using RF24Network. Put this sketch
* on one node, and helloworld_rx.pde on the other. Tx will send
* Rx a nice message every 2 seconds which rx will print out for us.
*/
/**
* @example helloworld_rx.ino
*
* Simplest possible example of using RF24Network. Put this sketch
* on one node, and helloworld_tx.pde on the other. Tx will send
* Rx a nice message every 2 seconds which rx will print out for us.
*/
/**
* @example Network_Ping.ino
*
* Example to give users an understanding of addressing and topology in the mesh network
* Using this sketch, each node will send a ping to the base every
* few seconds. The RF24Network library will route the message across
* the mesh to the correct node.
*
*/
/**
* @example Network_Ping_Sleep.ino
*
* Example: This is almost exactly the same as the Network_Ping example, but with use
* of the integrated sleep mode.
*
* This example demonstrates how nodes on the network utilize sleep mode to conserve power. For example,
* the radio itself will draw about 13.5mA in receive mode. In sleep mode, it will use as little as 22ua (.000022mA)
* of power when not actively transmitting or receiving data. In addition, the Arduino is powered down as well,
* dropping network power consumption dramatically compared to previous capabilities. <br>
* Note: Sleeping nodes generate traffic that will wake other nodes up. This may be mitigated with further modifications. Sleep
* payloads are currently always routed to the master node, which will wake up intermediary nodes. Routing nodes can be configured
* to go back to sleep immediately.
* The displayed millis() count will give an indication of how much a node has been sleeping compared to the others, as millis() will
* not increment while a node sleeps.
*<br>
* - Using this sketch, each node will send a ping to every other node in the network every few seconds.<br>
* - The RF24Network library will route the message across the mesh to the correct node.<br>
*
*/
/**
* @example sensornet.pde
*
* Example of a sensor network.
* This sketch demonstrates how to use the RF24Network library to
* manage a set of low-power sensor nodes which mostly sleep but
* awake regularly to send readings to the base.
*/
/**
* @mainpage Network Layer for RF24 Radios
*
* This class implements an <a href="http://en.wikipedia.org/wiki/Network_layer">OSI Network Layer</a> using nRF24L01(+) radios driven
* by the newly optimized <a href="http://tmrh20.github.com/RF24/">RF24</a> library fork.
*
* @section Purpose Purpose/Goal
*
* Original: Create an alternative to ZigBee radios for Arduino communication.
* New: Enhance the current functionality for maximum efficiency, reliability, and speed
*
* Xbees are excellent little radios, backed up by a mature and robust standard
* protocol stack. They are also expensive.
*
* For many Arduino uses, they seem like overkill. So I am working to build
* an alternative using nRF24L01 radios. Modules are available for less than
* $6 from many sources. With the RF24Network layer, I hope to cover many
* common communication scenarios.
*
* Please see the @ref Zigbee page for a comparison against the ZigBee protocols
*
* @section Features Features
*
* The layer provides:
* @li <b>New</b> (2014): Dual headed operation: The use of dual radios for busy routing nodes or the master node enhances throughput and decreases errors. See the <a href="Tuning.html">Tuning</a> section.
* @li <b>New</b> (2014): Extended timeouts and staggered timeout intervals. The new txTimeout variable allows fully automated extended timeout periods via auto-retry/auto-reUse of payloads.
* @li <b>New</b> (2014): Optimization to the core library provides improvements to reliability, speed and efficiency. See https://tmrh20.github.io/RF24 for more info.
* @li <b>New</b> (2014): Built in sleep mode using interrupts. (Still under development. (Enable via RF24Network_config.h))
* @li Host Addressing. Each node has a logical address on the local network.
* @li Message Forwarding. Messages can be sent from one node to any other, and
* this layer will get them there no matter how many hops it takes.
* @li Ad-hoc Joining. A node can join a network without any changes to any
* existing nodes.
*
* The layer does not (yet) provide:
* @li Fragmentation/reassembly. Ability to send longer messages and put them
* all back together before exposing them up to the app.
* @li Dynamic address assignment.
*
* @section More How to learn more
*
* @li <a href="classRF24Network.html">RF24Network Class Documentation</a>
* @li <a href="Tuning.html"> Performance and Data Loss: Tuning the Network</a>
* @li <a href="http://tmrh20.blogspot.com/2014/03/high-speed-data-transfers-and-wireless.html">My Blog: RF24 Optimization Overview</a>
* @li <a href="http://tmrh20.blogspot.com/2014/03/arduino-radiointercomwireless-audio.html">My Blog: RF24 Wireless Audio</a>
* @li <a href="http://tmrh20.github.com/RF24/">RF24: Underlying radio driver</a>
* @li <a href="https://github.com/TMRh20/RF24Network/archive/master.zip">Download Current Package</a>
* @li <a href="examples.html">Examples Page</a>. Start with <a href="helloworld_rx_8ino-example.html">helloworld_rx</a> and <a href="helloworld_tx_8ino-example.html">helloworld_tx</a>.
* @li <a href="http://maniacbug.github.com/RF24/">RF24: Original Author</a>
* @section Topology Topology for Mesh Networks using nRF24L01(+)
*
* This network layer takes advantage of the fundamental capability of the nRF24L01(+) radio to
* listen actively to up to 6 other radios at once. The network is arranged in a
* <a href="http://en.wikipedia.org/wiki/Network_Topology#Tree">Tree Topology</a>, where
* one node is the base, and all other nodes are children either of that node, or of another.
* Unlike a true mesh network, multiple nodes are not connected together, so there is only one
* path to any given node.
*
* @section Octal Octal Addressing and Topology
*
* Each node must be assigned an 15-bit address by the administrator. This address exactly
* describes the position of the node within the tree. The address is an octal number. Each
* digit in the address represents a position in the tree further from the base.
*
* @li Node 00 is the base node.
* @li Nodes 01-05 are nodes whose parent is the base.
* @li Node 021 is the second child of node 01.
* @li Node 0321 is the third child of node 021, an so on.
* @li The largest node address is 05555, so 3,125 nodes are allowed on a single channel.
* An example topology is shown below, with 5 nodes in direct communication with the master node,
* and multiple leaf nodes spread out at a distance, using intermediate nodes to reach other nodes.
*
*| | | 00 | | | 00 | | | | Master Node (00) |
*|---|----|----|----|----|----|----|----|----|-----------------------------------------------------|
*| | | 01 | | | 04 | | | | 1st level children of master (00) |
*| | 011| | 021| | |014 | | | 2nd level children of master. Children of 1st level.|
*|111| | | 121| 221| | | 114| | 3rd level children of master. Children of 2nd level.|
*| | | | |1221| |1114|2114|3114| 4th level children of master. Children of 3rd level.|
*
* @section Routing How routing is handled
*
* When sending a message using RF24Network::write(), you fill in the header with the logical
* node address. The network layer figures out the right path to find that node, and sends
* it through the system until it gets to the right place. This works even if the two nodes
* are far separated, as it will send the message down to the base node, and then back out
* to the final destination.
*
* All of this work is handled by the RF24Network::update() method, so be sure to call it
* regularly or your network will miss packets.
*
* @section Startup Starting up a node
*
* When a node starts up, it only has to contact its parent to establish communication.
* No direct connection to the Base node is needed. This is useful in situations where
* relay nodes are being used to bridge the distance to the base, so leaf nodes are out
* of range of the base.
*
* @section Directionality Directionality
*
* By default all nodes are always listening, so messages will quickly reach
* their destination.
*
* You may choose to sleep any nodes on the network if using interrupts. This is useful in a
* case where the nodes are operating on batteries and need to sleep. This greatly decreases
* the power requirements for a sensor network. The leaf nodes can sleep most of the time,
* and wake every few minutes to send in a reading. Routing nodes can be triggered to wake up
* whenever a payload is received See sleepNode() in the class documentation, and RFNetwork_config.h
* to enable sleep mode.
*
*
* @page Zigbee Comparison to ZigBee
*
* This network layer is influenced by the design of ZigBee, but does not implement it
* directly.
*
* @section Advantage Which is better?
*
* ZigBee is a much more robust, feature-rich set of protocols, with many different vendors
* providing compatible chips.
*
* RF24Network is cheap. While ZigBee radios are well over $20, nRF24L01 modules can be found
* for under $2. My personal favorite is
* <a href="http://www.mdfly.com/index.php?main_page=product_info&products_id=82">MDFly RF-IS2401</a>.
*
* @section Contrast Similiarities & Differences
*
* Here are some comparisons between RF24Network and ZigBee.
*
* @li Both networks support Star and Tree topologies. Only Zigbee supports a true mesh.
* @li In ZigBee networks, only leaf nodes can sleep
* @li ZigBee nodes are configured using AT commands, or a separate Windows application.
* RF24 nodes are configured by recompiliing the firmware or writing to EEPROM.
*
* @section NodeNames Node Naming
*
* @li Leaf node: A node at the outer edge of the network with no children. ZigBee calls it
* an End Device node.
* @li Relay node: A node which has both parents and children, and relays messages from one
* to the other. ZigBee calls it a Router.
* @li Base node. The top of the tree node with no parents, only children. Typically this node
* will bridge to another kind of network like Ethernet. ZigBee calls it a Co-ordinator node.
*
* @page Tuning Performance and Data Loss: Tuning the Network
* Tips and examples for tuning the network and Dual-head operation.
*
* @section TuningOverview Tuning Overview
* The RF24 radio modules are generally only capable of either sending or receiving data at any given
* time, but have built-in auto-retry mechanisms to prevent the loss of data. These values are adjusted
* automatically by the library on startup, but can be further adjusted to reduce data loss, and
* thus increase throughput of the network. This page is intended to provide a general overview of its
* operation within the context of the network library, and provide guidance for adjusting these values.
*
* @section RetryTiming Auto-Retry Timing
*
* The core radio library provides the functionality of adjusting the internal auto-retry interval of the
* radio modules. In the network configuration, the radios can be set to automatically retry failed
* transmissions at intervals ranging anywhere from 500us (.5ms) up to 4000us (4ms). When operating any
* number of radios larger than two, it is important to stagger the assigned intervals, to prevent the
* radios from interfering with each other at the radio frequency (RF) layer.
*
* The library should provide fairly good working values, as it simply staggers the assigned values within
* groups of radios in direct communication. This value can be set manually by calling radio.setRetries(X,15);
* and adjusting the value of X from 1 to 15 (steps of 250us).
*
* @section AutoRetry Auto-Retry Count and Extended Timeouts
*
* The core radio library also provides the ability to adjust the internal auto-retry count of the radio
* modules. The default setting is 15 automatic retries per payload, and can be extended by configuring
* the network.txTimeout variable. This default retry count should generally be left at 15, as per the
* example in the above section. The txTimeout variable is used to extend the retry count to a defined
* duration in milliseconds. An interval/retry setting of (15,15) will provide 15 retrys at intervals of
* 4ms, taking up to 60ms per payload.
*
* The library now provides staggered timout periods by default, but they can also be adjusted on a per-node
* basis. See the network.txTimeout variable.
* Timeout periods of extended duration (500+) will generally not help when payloads are failing due to data
* collisions, it will only extend the duration of the errors. Extended duration timeouts should generally only
* be configured on leaf nodes that do not receive data, or on a dual-headed node.
*
* @section Examples
*
* <b>Example 1:</b> Network with master node and three leaf nodes that send data to the master node. None of the leaf
* nodes need to receive data.
*
* a: Master node uses default configuration<br>
* b: Leaf nodes can be configured with extended timeout periods to ensure reception by the master.<br>
* c:
* @code
* Leaf 01: network.txTimeout = 500; Leaf 02: network.txTimeout = 573; Leaf 03: network.txTimeout = 653;
* @endcode
* This configuration will provide a reduction in errors, as the timeouts have been extended, and are staggered
* between devices.
*
*
* <b>Example 2:</b> Network with master node and three leaf nodes that send data to the master node. The second leaf
* node needs to receive configuration data from the master at set intervals of 1 second, and send data back to the
* master node. The other leaf nodes will send basic sensor information every few seconds, and a few dropped payloads
* will not affect the operation greatly.
*
* a: Master node configured with extended timeouts of .5 seconds, and increased retry delay:
* @code
* radio.setRetries(11,15);
* network.txTimeout(500);
* @endcode
* b: Second leaf node configured with a similar timeout period and retry delay:
* @code
* radio.setRetries(8,15);
* network.txTimeout(553);
* @endcode
* c: First and third leaf nodes configured with default timeout periods or slightly increased timout periods.
*
* @section DualHead Dual Headed Operation
*
* The library now supports a dual radio configuration to further enhance network performance, while reducing errors on
* busy networks. Master nodes or relay nodes with a large number of child nodes can benefit greatly from a dual headed
* configuration, since one radio is used for receiving, and the other entirely for transmission.
*
* To configure a dual headed node:
* 1. Edit the RF24Network_config.h file, and uncomment #define DUAL_HEAD_RADIO
* 2. Connect another radio, using the same MOSI, MISO, and SCK lines.
* 3. Choose another two pins to use for CE and CS on the second radio. Connect them.
* 4. Setup the radio and network like so:
*
* @code
* RF24 radio(7,8); // Using CE (7) and CS (8) for first radio
* RF24 radio1(4,5); // Using CE (4) and CS (5) for second radio
* RF24Network network(radio,radio1); // Set up the network using both radios
*
* Then in setup(), call radio.begin(); and radio1.begin(); before network.begin();
* @endcode
*
* 5. Upload to MCU. The node will now use the first radio to receive data, and radio1 to transmit, preventing data loss on a busy network.
* 6. Re-comment the #define in the config file as required if configuring other single-headed radios.
*
*
* Any node can be configured in dual-head mode.
*
*
*/
#endif // __RF24NETWORK_H__