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