RF24Mesh - Automated Networking for nrf24L01 & nrf52x radios v2.0.2
2024 - A user friendly mesh overlay for sensor neworks using RF24Network
Loading...
Searching...
No Matches
RF24Mesh.cpp
Go to the documentation of this file.
1
7#include "RF24Mesh.h"
8#include "RF24Mesh_config.h"
9#if defined(__linux) && !defined(__ARDUINO_X86__)
10 #include <fstream>
11#endif
12
13template<class network_t, class radio_t>
14ESBMesh<network_t, radio_t>::ESBMesh(radio_t& _radio, network_t& _network) : radio(_radio), network(_network)
15{
16 setCallback(NULL);
17 meshStarted = false;
18#if !defined(MESH_NOMASTER)
19 addrMemAllocated = false;
20#endif
21}
22
23/*****************************************************/
24
25template<class network_t, class radio_t>
26bool ESBMesh<network_t, radio_t>::begin(uint8_t channel, rf24_datarate_e data_rate, uint32_t timeout)
27{
28 //delay(1); // Found problems w/SPIDEV & ncurses. Without this, getch() returns a stream of garbage
29 if (meshStarted) {
30 radio.stopListening();
31 }
32 meshStarted = true;
33 if (!radio.begin())
34 return 0;
35 radio.setChannel(channel);
36 radio.setDataRate(data_rate);
37 network.returnSysMsgs = true;
38
39 if (getNodeID() > 0) { //Not master node
40 if (renewAddress(timeout) == MESH_DEFAULT_ADDRESS) {
41 return false;
42 }
43 }
44 else {
45#if !defined(MESH_NOMASTER)
46 if (!addrMemAllocated) {
47 addrMemAllocated = true;
48 addrList = (addrListStruct*)malloc((MESH_MEM_ALLOC_SIZE * sizeof(addrListStruct)));
49 addrListTop = 0;
50 loadDHCP();
51 }
52#endif
53 mesh_address = 0;
54 network.begin(mesh_address);
55 }
56
57 return true;
58}
59
60/*****************************************************/
61
62template<class network_t, class radio_t>
64{
65 uint8_t type = network.update();
66 if (mesh_address == MESH_DEFAULT_ADDRESS) return type;
67
68#if !defined(MESH_NOMASTER)
69 if (type == NETWORK_REQ_ADDRESS) {
70 doDHCP = 1;
71 }
72
73 if (!getNodeID()) {
74 if ((type == MESH_ADDR_LOOKUP || type == MESH_ID_LOOKUP)) {
75 RF24NetworkHeader* header = (RF24NetworkHeader*)(&network.frame_buffer);
76 header->to_node = header->from_node;
77
78 int16_t returnAddr = 0;
79 if (type == MESH_ADDR_LOOKUP) {
80 returnAddr = getAddress(network.frame_buffer[sizeof(RF24NetworkHeader)]);
81 network.write(*header, &returnAddr, sizeof(returnAddr));
82 }
83 else {
84 int16_t addr = 0;
85 memcpy(&addr, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(addr));
86 returnAddr = getNodeID(addr);
87 network.write(*header, &returnAddr, sizeof(returnAddr));
88 }
89 }
90 else if (type == MESH_ADDR_RELEASE) {
91 uint16_t* fromAddr = (uint16_t*)network.frame_buffer;
92 releaseAddress(*fromAddr);
93 }
94 }
95#endif
96
97 return type;
98}
99
100/*****************************************************/
101
102template<class network_t, class radio_t>
103bool ESBMesh<network_t, radio_t>::write(uint16_t to_node, const void* data, uint8_t msg_type, size_t size)
104{
105 if (mesh_address == MESH_DEFAULT_ADDRESS) return 0;
106
107 RF24NetworkHeader header(to_node, msg_type);
108 return network.write(header, data, size);
109}
110
111/*****************************************************/
112
113template<class network_t, class radio_t>
114bool ESBMesh<network_t, radio_t>::write(const void* data, uint8_t msg_type, size_t size, uint8_t nodeID)
115{
116 if (mesh_address == MESH_DEFAULT_ADDRESS) return 0;
117
118 int16_t toNode = 0;
119 uint32_t lookupTimeout = millis() + MESH_WRITE_TIMEOUT;
120 uint32_t retryDelay = 5;
121
122 if (nodeID) {
123 while ((toNode = getAddress(nodeID)) < 0) {
124 if (millis() > lookupTimeout || toNode == -2) {
125 return 0;
126 }
127 retryDelay += 10;
128 delay(retryDelay);
129 }
130 }
131 return write(toNode, data, msg_type, size);
132}
133
134/*****************************************************/
135
136template<class network_t, class radio_t>
138{
139 radio.stopListening();
140 radio.setChannel(_channel);
141 radio.startListening();
142}
143
144/*****************************************************/
145
146template<class network_t, class radio_t>
148{
149 network.networkFlags = allow ? network.networkFlags & ~FLAG_NO_POLL : network.networkFlags | FLAG_NO_POLL;
150}
151
152/*****************************************************/
153
154template<class network_t, class radio_t>
156{
157
158 if (!_nodeID) return false;
159 if (mesh_address == MESH_DEFAULT_ADDRESS) return false;
160
161// Connection check via parent node
162#if RF24MESH_CONN_CHECK_TYPE == RF24MESH_CONN_CHECK_PARENT
163 RF24NetworkHeader header;
164 header.to_node = network.parent();
165 header.type = NETWORK_PING;
166 for (uint8_t i = 0; i < MESH_CONNECTION_CHECK_ATTEMPTS; ++i) {
167 if (network.write(header, 0, 0)) {
168 return true;
169 }
170 }
171 return false;
172
173#else // Connection check via master node
174 // getAddress() doesn't use auto-ack; check connectivity multiple times
175 for (uint8_t i = 0; i < MESH_CONNECTION_CHECK_ATTEMPTS; ++i) {
176
177 int16_t result = getAddress(_nodeID);
178 switch (result) {
179 case -2: return false; // Address not found in list or is default
180 case -1: continue; // Write failed or timed out
181 case 0: return false; // This is a master node
182 default:
183 if ((uint16_t)result == mesh_address) { // Successful address lookup if result == RF24Network address
184 return true;
185 }
186 break;
187 }
188 }
189 return false;
190#endif
191}
192
193/*****************************************************/
194
195template<class network_t, class radio_t>
197{ // Master will return and send 00 address for a nodeID with address 0, -1 if not found
198
199 //if (nodeID == _nodeID) return mesh_address;
200 if (!nodeID) return 0;
201 if (mesh_address == MESH_DEFAULT_ADDRESS) return -2;
202
203// Lets say 0 if nodeID 0, -1 if write failed or timed out, -2 if not found in list or address is default,
204#if !defined(MESH_NOMASTER)
205 if (!getNodeID()) { //Master Node
206 for (uint8_t i = 0; i < addrListTop; i++) {
207 if (addrList[i].nodeID == nodeID) {
208 return addrList[i].address;
209 }
210 }
211 return -2;
212 }
213#endif
214
215 RF24NetworkHeader header(00, MESH_ADDR_LOOKUP);
216 if (network.write(header, &nodeID, sizeof(nodeID))) {
217 uint32_t timer = millis();
218 while (network.update() != MESH_ADDR_LOOKUP) {
220 if (millis() - timer > MESH_LOOKUP_TIMEOUT) {
221 return -1;
222 }
223 }
224 int16_t address = 0;
225 memcpy(&address, network.frame_buffer + sizeof(RF24NetworkHeader), sizeof(address));
226 return address;
227 }
228 return -1;
229}
230
231/*****************************************************/
232
233template<class network_t, class radio_t>
235{
236 if (address == MESH_BLANK_ID) return _nodeID;
237 if (address == 0) return 0;
238 if (mesh_address == MESH_DEFAULT_ADDRESS) return -2;
239
240#if !defined(MESH_NOMASTER)
241 if (!mesh_address) { //Master Node
242 for (uint8_t i = 0; i < addrListTop; i++) {
243 if (addrList[i].address == address) {
244 return addrList[i].nodeID;
245 }
246 }
247 return -2;
248 }
249#endif
250
251 RF24NetworkHeader header(00, MESH_ID_LOOKUP);
252 if (network.write(header, &address, sizeof(address))) {
253 uint32_t timer = millis();
254 while (network.update() != MESH_ID_LOOKUP) {
256 if (millis() - timer > MESH_LOOKUP_TIMEOUT) return -1;
257 }
258 int16_t ID = 0;
259 memcpy(&ID, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(ID));
260 return ID;
261 }
262 return -1;
263}
264
265/*****************************************************/
266
267template<class network_t, class radio_t>
268uint8_t ESBMesh<network_t, radio_t>::getLevel(uint16_t address)
269{
270 uint8_t count = 0;
271 while (address) {
272 address >>= 3;
273 count++;
274 }
275 return count;
276}
277
278/*****************************************************/
279
280template<class network_t, class radio_t>
282{
283 radio.stopListening();
285 mesh_address = MESH_DEFAULT_ADDRESS;
286}
287
288/*****************************************************/
289
290template<class network_t, class radio_t>
292{
293 if (mesh_address == MESH_DEFAULT_ADDRESS) return 0;
294
295 RF24NetworkHeader header(00, MESH_ADDR_RELEASE);
296 if (network.write(header, 0, 0)) {
297 beginDefault();
298 return 1;
299 }
300 return 0;
301}
302
303/*****************************************************/
304
305#ifndef MESH_NOMASTER
306template<class network_t, class radio_t>
308{
309 for (uint8_t i = 0; i < addrListTop; ++i) {
310 if (addrList[i].address == address) {
311 addrList[i].address = 0;
312 return true;
313 }
314 }
315 return false;
316}
317#endif
318
319/*****************************************************/
320
321template<class network_t, class radio_t>
323{
324 if (radio.available()) network.update();
325
326 uint8_t reqCounter = 0;
327 uint8_t totalReqs = 0;
328
329 beginDefault();
330
331 uint32_t start = millis();
332 while (!requestAddress(reqCounter)) {
333 if (millis() - start > timeout) break;
334
335 uint32_t timeoutInternal = 50 + ((totalReqs + 1) * (reqCounter + 1)) * 2;
336 uint32_t startInternal = millis();
337 while (millis() - startInternal < timeoutInternal) {
339 delay(1);
340 }
341 reqCounter++;
342 reqCounter = reqCounter % 4;
343 totalReqs++;
344 totalReqs = totalReqs % 10;
345 }
346 return mesh_address;
347}
348
349/*****************************************************/
350
351template<class network_t, class radio_t>
353{
354 RF24NetworkHeader header(MESH_MULTICAST_ADDRESS, NETWORK_POLL);
355 //Find another radio, starting with level 0 multicast
356 IF_RF24MESH_DEBUG(printf_P(PSTR("MSH Poll Level %d\n"), level));
357 network.multicast(header, 0, 0, level);
358
359 uint32_t timeout = millis() + 55;
360#define MESH_MAXPOLLS 4
361 uint16_t contactNode[MESH_MAXPOLLS];
362 uint8_t pollCount = 0;
363
364 while (millis() < timeout && pollCount < MESH_MAXPOLLS) {
365#if defined(RF24MESH_DEBUG)
366 bool goodSignal = radio.testRPD();
367#endif
368 if (network.update() == NETWORK_POLL) {
369 uint16_t contact = 0;
370 memcpy(&contact, &network.frame_buffer[0], sizeof(contact));
371
372 // Drop duplicate polls to help prevent duplicate requests
373 bool isDupe = false;
374 for (uint8_t i = 0; i < pollCount; ++i) {
375 if (contact == contactNode[i]) {
376 isDupe = true;
377 break;
378 }
379 }
380 if (!isDupe) {
381 contactNode[pollCount] = contact;
382 ++pollCount;
383 IF_RF24MESH_DEBUG(printf_P(PSTR("MSH Poll %c -64dbm from 0%o \n"), (goodSignal ? '>' : '<'), contact));
384 }
385
386 } // end if
387
389 } // end while
390
391 IF_RF24MESH_DEBUG(printf_P(PSTR("MSH Polls from level %d: %d\n"), level, pollCount));
392
393 if (!pollCount) return 0;
394
395 for (uint8_t i = 0; i < pollCount; i++) {
396
397 bool gotResponse = 0;
398
399 // Request an address via the contact node
400 header.type = NETWORK_REQ_ADDRESS;
401 header.reserved = _nodeID;
402 header.to_node = contactNode[i];
403
404 // Do a direct write (no ack) to the contact node. Include the nodeId and address.
405 network.write(header, 0, 0, contactNode[i]);
406
407 IF_RF24MESH_DEBUG(printf_P(PSTR("MSH Request address from: 0%o\n"), contactNode[i]));
408
409 timeout = millis() + 225;
410
411 while (millis() < timeout) {
412 if (network.update() == NETWORK_ADDR_RESPONSE) {
413 if (network.frame_buffer[7] == _nodeID) {
414 uint16_t newAddy = 0;
415 memcpy(&newAddy, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(newAddy));
416 uint16_t mask = 0xFFFF;
417 newAddy &= ~(mask << (3 * getLevel(contactNode[i]))); // Get the level of contact node. Multiply by 3 to get the number of bits to shift (3 per digit)
418 if (newAddy == contactNode[i]) { // Then shift the mask by this much, and invert it bitwise. Apply the mask to the newly received
419 gotResponse = 1; // address to evalute whether 'subnet' of the assigned address matches the contact node address.
420 break;
421 }
422 }
423 }
425 }
426
427 if (!gotResponse) {
428 continue;
429 }
430
431 uint16_t newAddress = 0;
432 memcpy(&newAddress, network.frame_buffer + sizeof(RF24NetworkHeader), sizeof(newAddress));
433
434 IF_RF24MESH_DEBUG(printf_P(PSTR("Set address: Current: 0%o New: 0%o\n"), mesh_address, newAddress));
435 mesh_address = newAddress;
436
437 radio.stopListening();
438 network.begin(mesh_address);
439
440 // getNodeID() doesn't use auto-ack; do a double-check to manually retry 1 more time
441 if (getNodeID(mesh_address) != _nodeID) {
442 if (getNodeID(mesh_address) != _nodeID) {
443 beginDefault();
444 continue;
445 }
446 }
447 return 1;
448 } // end for
449
450 return 0;
451}
452
453/*****************************************************/
454
455template<class network_t, class radio_t>
457{
458 _nodeID = nodeID;
459}
460
461/*****************************************************/
462#if !defined(MESH_NOMASTER)
463
464template<class network_t, class radio_t>
465void ESBMesh<network_t, radio_t>::setStaticAddress(uint8_t nodeID, uint16_t address)
466{
467 setAddress(nodeID, address);
468}
469
470/*****************************************************/
471
472template<class network_t, class radio_t>
473void ESBMesh<network_t, radio_t>::setAddress(uint8_t nodeID, uint16_t address, bool searchBy)
474{
475 //Look for the node in the list
476 for (uint8_t i = 0; i < addrListTop; i++) {
477 if (searchBy == false) {
478 if (addrList[i].nodeID == nodeID) {
479 addrList[i].address = address;
480 #if defined(__linux) && !defined(__ARDUINO_X86__)
481 saveDHCP();
482 #endif
483 return; //Found & set, complete
484 }
485 }
486 else { // Search by address, set the nodeID
487 if (addrList[i].address == address) {
488 //printf("*** Addr 0%o Found, reassign fr ID %d to ID %d ***\n", addrList[i].address, addrList[i].nodeID, nodeID);
489 addrList[i].nodeID = nodeID;
490 #if defined(__linux) && !defined(__ARDUINO_X86__)
491 saveDHCP();
492 #endif
493 return;
494 }
495 }
496 }
497
498 if (addrListTop > 0 && addrListTop % MESH_MEM_ALLOC_SIZE == 0) {
499 addrList = (addrListStruct*)realloc(addrList, (addrListTop + MESH_MEM_ALLOC_SIZE) * sizeof(addrListStruct));
500 }
501 addrList[addrListTop].address = address;
502 addrList[addrListTop++].nodeID = nodeID; //Set the value AND increment Top without another line of code
503 #if defined(__linux) && !defined(__ARDUINO_X86__)
504 saveDHCP();
505 #endif
506}
507
508/*****************************************************/
509
510template<class network_t, class radio_t>
512{
513
514 #if defined(__linux) && !defined(__ARDUINO_X86__)
515 std::ifstream infile("dhcplist.txt", std::ifstream::binary);
516 if (!infile) return;
517
518 infile.seekg(0, infile.end);
519 int length = infile.tellg();
520 infile.seekg(0, infile.beg);
521
522 addrListStruct tmpNode;
523
524 for (uint8_t i = 0; i < (length / sizeof(addrListStruct)); i++) {
525 infile.read((char*)&tmpNode, sizeof(addrListStruct));
526 setAddress(tmpNode.nodeID, tmpNode.address);
527 }
528 infile.close();
529 #endif
530}
531
532/*****************************************************/
533
534template<class network_t, class radio_t>
536{
537 #if defined(__linux) && !defined(__ARDUINO_X86__)
538 std::ofstream outfile("dhcplist.txt", std::ofstream::binary | std::ofstream::trunc);
539
540 for (int i = 0; i < addrListTop; i++) {
541 outfile.write((char*)&addrList[i], sizeof(addrListStruct));
542 }
543 outfile.close();
544 #endif // __linux & not X86
545}
546
547/*****************************************************/
548
549template<class network_t, class radio_t>
551{
552 if (doDHCP)
553 doDHCP = false;
554 else
555 return;
556
557 RF24NetworkHeader header;
558 memcpy(&header, network.frame_buffer, sizeof(RF24NetworkHeader));
559
560 uint16_t newAddress;
561
562 // Get the unique id of the requester (ID is in header.reserved)
563 if (!header.reserved || header.type != NETWORK_REQ_ADDRESS) {
564 IF_RF24MESH_DEBUG(printf_P(PSTR("MSH Invalid id or type rcvd\n")));
565 return;
566 }
567
568 uint16_t fwd_by = 0;
569 uint8_t shiftVal = 0;
570 bool extraChild = false;
571
572 if (header.from_node != MESH_DEFAULT_ADDRESS) {
573 fwd_by = header.from_node;
574 uint16_t m = fwd_by;
575 uint8_t count = 0;
576
577 while (m) { //Octal addresses convert nicely to binary in threes. Address 03 = B011 Address 033 = B011011
578 m >>= 3; //Find out how many digits are in the octal address
579 count += 3;
580 }
581 shiftVal = count; //Now we know how many bits to shift when adding a child node 1-5 (B001 to B101) to any address
582 }
583 else {
584 //If request is coming from level 1, add an extra child to the master
585 extraChild = 1;
586 }
587
588 // IF_RF24MESH_DEBUG(printf_P(PSTR("%u: MSH Rcv addr req from_id %d\n"), millis(), header.reserved));
589
590 for (int i = MESH_MAX_CHILDREN + extraChild; i > 0; i--) { // For each of the possible addresses (5 max)
591
592 bool found = false;
593 newAddress = fwd_by | (i << shiftVal);
594
595 if (newAddress == MESH_DEFAULT_ADDRESS) continue;
596
597 for (uint8_t i = 0; i < addrListTop; i++) {
598 IF_RF24MESH_DEBUG_MINIMAL(printf_P(PSTR("ID: %d ADDR: 0%o\n"), addrList[i].nodeID, addrList[i].address));
599 if (addrList[i].address == newAddress && addrList[i].nodeID != header.reserved) {
600 found = true;
601 break;
602 }
603 } // 3 conditions: 1. address in list = assigned to somebody else (bad); 2. address in list = assigned to this nodeID (ok); 3. address not in list (ok)
604
605 if (!found) {
606 header.type = NETWORK_ADDR_RESPONSE;
607 header.to_node = header.from_node;
608 //This is a routed request to 00
609
610 setAddress(header.reserved, newAddress);
611 // without this delay, address renewal fails for children with slower execution speed
612 #if defined(SLOW_ADDR_POLL_RESPONSE)
613 delay(SLOW_ADDR_POLL_RESPONSE);
614 #endif // defined (SLOW_ADDR_POLL_RESPONSE)
615
616 if (header.from_node != MESH_DEFAULT_ADDRESS) { //Is NOT node 01 to 05
617 //delay(2);
618 if (!network.write(header, &newAddress, sizeof(newAddress))) {
619 network.write(header, &newAddress, sizeof(newAddress));
620 }
621 }
622 else {
623 //delay(2);
624 network.write(header, &newAddress, sizeof(newAddress), header.to_node);
625 }
626
627 IF_RF24MESH_DEBUG(printf_P(PSTR("Sent to 0%o phys: 0%o new: 0%o id: %d\n"), header.to_node, MESH_DEFAULT_ADDRESS, newAddress, header.reserved));
628 break;
629 }
630 else {
631 IF_RF24MESH_DEBUG(printf_P(PSTR("not allocated\n")));
632 }
633 } // end for
634}
635
636/*****************************************************/
637
638#endif // !MESH_NOMASTER
639
640template<class network_t, class radio_t>
641void ESBMesh<network_t, radio_t>::setCallback(void (*meshCallback)(void))
642{
643
644 this->meshCallback = meshCallback;
645}
646
647/*****************************************************/
648
649// ensure the compiler is aware of the possible datatype for the template class
650template class ESBMesh<ESBNetwork<RF24>, RF24>;
651#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840)
652template class ESBMesh<ESBNetwork<nrf_to_nrf>, nrf_to_nrf>;
653#endif
#define MESH_MAXPOLLS
#define MESH_ADDR_LOOKUP
Definition RF24Mesh.h:25
#define MESH_ID_LOOKUP
Definition RF24Mesh.h:27
#define MESH_CALLBACK
Definition RF24Mesh.h:260
#define MESH_ADDR_RELEASE
Definition RF24Mesh.h:26
#define MESH_BLANK_ID
Definition RF24Mesh.h:29
#define MESH_MULTICAST_ADDRESS
#define IF_RF24MESH_DEBUG(x)
#define MESH_MAX_CHILDREN
Set 1 to 4 (Default: 4) Restricts the maximum children per node.
#define MESH_LOOKUP_TIMEOUT
How long to wait in ms for a response during individual address lookups.
#define IF_RF24MESH_DEBUG_MINIMAL(x)
#define MESH_DEFAULT_ADDRESS
#define MESH_CONNECTION_CHECK_ATTEMPTS
Number of attempts to verify a connection.
#define MESH_MEM_ALLOC_SIZE
master node memory allocation
#define MESH_WRITE_TIMEOUT
How long RF24Mesh::write() retries address lookups before timing out. Allows multiple attempts.
int16_t getNodeID(uint16_t address=MESH_BLANK_ID)
Definition RF24Mesh.cpp:234
void setStaticAddress(uint8_t nodeID, uint16_t address)
Definition RF24Mesh.cpp:465
void loadDHCP()
Definition RF24Mesh.cpp:511
uint16_t renewAddress(uint32_t timeout=MESH_RENEWAL_TIMEOUT)
Reconnect to the mesh and renew the current RF24Network address.
Definition RF24Mesh.cpp:322
void setChild(bool allow)
Definition RF24Mesh.cpp:147
bool checkConnection()
Definition RF24Mesh.cpp:155
bool begin(uint8_t channel=MESH_DEFAULT_CHANNEL, rf24_datarate_e data_rate=RF24_1MBPS, uint32_t timeout=MESH_RENEWAL_TIMEOUT)
Definition RF24Mesh.cpp:26
void DHCP()
Definition RF24Mesh.cpp:550
bool releaseAddress()
Definition RF24Mesh.cpp:291
void saveDHCP()
Definition RF24Mesh.cpp:535
void setCallback(void(*meshCallback)(void))
Definition RF24Mesh.cpp:641
uint8_t update()
Definition RF24Mesh.cpp:63
ESBMesh(radio_t &_radio, network_t &_network)
Definition RF24Mesh.cpp:14
void setNodeID(uint8_t nodeID)
Definition RF24Mesh.cpp:456
bool write(const void *data, uint8_t msg_type, size_t size, uint8_t nodeID=0)
Definition RF24Mesh.cpp:114
void setChannel(uint8_t _channel)
Definition RF24Mesh.cpp:137
void setAddress(uint8_t nodeID, uint16_t address, bool searchBy=false)
Definition RF24Mesh.cpp:473
int16_t getAddress(uint8_t nodeID)
Convert a nodeID into an RF24Network address.
Definition RF24Mesh.cpp:196
A struct for storing a nodeID and an address in a single element of the ESBMesh::addrList array.
Definition RF24Mesh.h:331
uint8_t nodeID
The nodeID of an network node (child)
Definition RF24Mesh.h:333
uint16_t address
The logical address of an network node (child)
Definition RF24Mesh.h:335