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