RF24Gateway - TCP/IP over RF24Network v2.0.0
TMRh20 - Pushing the practical limits of RF24 modules
Loading...
Searching...
No Matches
RF24Gateway.cpp
1/**
2 * @file RF24Gateway.cpp
3 *
4 * Class definitions for RF24Gateway
5 */
6
7#include "RF24Gateway.h"
8#include "RF24Mesh/RF24Mesh_config.h"
9
10/***************************************************************************************/
11
12template<class mesh_t, class network_t, class radio_t>
13ESBGateway<mesh_t, network_t, radio_t>::ESBGateway(radio_t& _radio, network_t& _network, mesh_t& _mesh) : radio(_radio), network(_network), mesh(_mesh)
14{
15 gotInterrupt = false;
16}
17
18/***************************************************************************************/
19
20template<class mesh_t, class network_t, class radio_t>
21void ESBGateway<mesh_t, network_t, radio_t>::begin(uint8_t nodeID, uint8_t _channel, rf24_datarate_e data_rate)
22{
23 mesh_enabled = true;
24 begin(true, mesh_enabled, 0, nodeID, data_rate, _channel);
25}
26
27/***************************************************************************************/
28
29template<class mesh_t, class network_t, class radio_t>
30void ESBGateway<mesh_t, network_t, radio_t>::begin(uint16_t address, uint8_t _channel, rf24_datarate_e data_rate, bool meshEnable, uint8_t nodeID)
31{
32 begin(0, mesh_enabled, address, nodeID, data_rate, _channel);
33}
34
35/***************************************************************************************/
36
37template<class mesh_t, class network_t, class radio_t>
38bool ESBGateway<mesh_t, network_t, radio_t>::begin(bool configTUN, bool meshEnable, uint16_t address, uint8_t mesh_nodeID, rf24_datarate_e data_rate, uint8_t _channel)
39{
40#if (DEBUG_LEVEL >= 1)
41 printf("GW Begin\n");
42 printf("Config Device address 0%o nodeID %d\n", address, mesh_nodeID);
43#endif
44 config_TUN = configTUN;
45
46 /// FIX
47
48 channel = _channel; // 97;
49
50 dataRate = data_rate;
51
52 configDevice(address);
53 mesh_enabled = meshEnable;
54 thisNodeID = mesh_nodeID;
55 thisNodeAddress = address;
56
57 if (meshEnable) {
58 // GW radio channel setting takes precedence over mesh_default_channel
59 if (channel == 97 && MESH_DEFAULT_CHANNEL != 97) {
60 channel = MESH_DEFAULT_CHANNEL;
61 }
62
63 if (!thisNodeAddress && !mesh_nodeID) {
64 mesh.setNodeID(0);
65 }
66 else {
67 if (!mesh_nodeID) {
68 mesh_nodeID = 253;
69 }
70 mesh.setNodeID(mesh_nodeID); // Try not to conflict with any low-numbered node-ids
71 }
72 mesh.begin(channel, data_rate);
73 thisNodeAddress = mesh.mesh_address;
74 }
75 else {
76 radio.begin();
77 delay(5);
78 const uint16_t this_node = address;
79 radio.setDataRate(dataRate);
80 radio.setChannel(channel);
81
82 network.begin(/*node address*/ this_node);
83 thisNodeAddress = this_node;
84 }
85 network.multicastRelay = 1;
86
87 //#if (DEBUG_LEVEL >= 1)
88 radio.printDetails();
89 //#endif
90
91 setupSocket();
92 loadRoutingTable();
93
94 return true;
95}
96
97/***************************************************************************************/
98
99template<class mesh_t, class network_t, class radio_t>
101{
102 std::ifstream infile("routing.txt", std::ifstream::in);
103 if (!infile) {
104 return;
105 }
106
107 std::string str;
108 std::string ip, mask, gw;
109 uint16_t count = 0;
110 std::string space = " ";
111
112 while (std::getline(infile, str)) {
113 size_t startLen = 0;
114 size_t subLen = str.find(space);
115 if (subLen != std::string::npos) {
116 ip = str.substr(0, subLen);
117 }
118 else {
119 continue;
120 }
121 startLen = subLen + 1;
122 subLen = str.find(space, startLen);
123 if (subLen != std::string::npos) {
124 subLen -= (startLen);
125 mask = str.substr(startLen, subLen);
126 }
127 else {
128 continue;
129 }
130 startLen = startLen + subLen + 1;
131 subLen = str.length() - (startLen);
132 gw = str.substr(startLen, subLen);
133
134 routingStruct[count].ip.s_addr = ntohl(inet_network(ip.c_str()));
135 routingStruct[count].mask.s_addr = ntohl(inet_network(mask.c_str()));
136 routingStruct[count].gw.s_addr = ntohl(inet_network(gw.c_str()));
137
138 count++;
139 if (count >= 256) {
140 break;
141 }
142 }
143 routingTableSize = count;
144
145 // for(int i=0; i<count; i++){
146 // std::cout << inet_ntoa(routingStruct[i].ip) << ";" << inet_ntoa(routingStruct[i].mask) << ";" << inet_ntoa(routingStruct[i].gw) << std::endl;
147 // }
148}
149
150/***************************************************************************************/
151
152template<class mesh_t, class network_t, class radio_t>
154{
155 return mesh_enabled;
156}
157
158/***************************************************************************************/
159
160template<class mesh_t, class network_t, class radio_t>
162{
163 std::string tunTapDevice = "tun_nrf24";
164 strcpy(tunName, tunTapDevice.c_str());
165
166 int flags;
167 if (config_TUN) {
168 flags = IFF_TUN | IFF_NO_PI | IFF_MULTI_QUEUE;
169 }
170 else {
171 flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE;
172 }
173 tunFd = allocateTunDevice(tunName, flags, address);
174#if DEBUG_LEVEL >= 1
175 if (tunFd >= 0) {
176 std::cout << "RF24Gw: Successfully attached to tun/tap device " << tunTapDevice << std::endl;
177 }
178 else {
179 std::cerr << "RF24Gw: Error allocating tun/tap interface: " << tunFd << std::endl;
180 exit(1);
181 }
182#endif
183 return tunFd;
184}
185
186/***************************************************************************************/
187
188template<class mesh_t, class network_t, class radio_t>
189int ESBGateway<mesh_t, network_t, radio_t>::allocateTunDevice(char* dev, int flags, uint16_t address)
190{
191 struct ifreq ifr;
192 int fd;
193
194 // open the device
195 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
196 return fd;
197 }
198
199 memset(&ifr, 0, sizeof(ifr));
200
201 ifr.ifr_flags = flags; // IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI
202
203 if (*dev) {
204 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
205 }
206
207 // Create device
208 if (ioctl(fd, TUNSETIFF, (void*)&ifr) < 0) {
209 // close(fd);
210 //#if (DEBUG_LEVEL >= 1)
211 std::cerr << "RF24Gw: Error: enabling TUNSETIFF" << std::endl;
212 std::cerr << "RF24Gw: If changing from TAP/TUN, run 'sudo ip link delete tun_nrf24' to remove the interface" << std::endl;
213 return -1;
214 //#endif
215 }
216
217 // Make persistent
218 if (ioctl(fd, TUNSETPERSIST, 1) < 0) {
219#if (DEBUG_LEVEL >= 1)
220 std::cerr << "RF24Gw: Error: enabling TUNSETPERSIST" << std::endl;
221#endif
222 return -1;
223 }
224
225 if (!config_TUN) {
226 struct sockaddr sap;
227 sap.sa_family = ARPHRD_ETHER;
228 ((char*)sap.sa_data)[4] = address;
229 ((char*)sap.sa_data)[5] = address >> 8;
230 ((char*)sap.sa_data)[0] = 0x52;
231 ((char*)sap.sa_data)[1] = 0x46;
232 ((char*)sap.sa_data)[2] = 0x32;
233 ((char*)sap.sa_data)[3] = 0x34;
234
235 // printf("Address 0%o first %u last %u\n",address,sap.sa_data[0],sap.sa_data[1]);
236 memcpy((char*)&ifr.ifr_hwaddr, (char*)&sap, sizeof(struct sockaddr));
237
238 if (ioctl(fd, SIOCSIFHWADDR, &ifr) < 0) {
239#if DEBUG_LEVEL >= 1
240 fprintf(stderr, "RF24Gw: Failed to set MAC address\n");
241#endif
242 }
243 }
244
245 strcpy(dev, ifr.ifr_name);
246 return fd;
247}
248
249/***************************************************************************************/
250
251template<class mesh_t, class network_t, class radio_t>
253{
254 struct ifreq ifr;
255 struct sockaddr_in sin;
256 int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
257 if (sockfd == -1) {
258 fprintf(stderr, "Could not get socket.\n");
259 return -1;
260 }
261
262 sin.sin_family = AF_INET;
263 // inet_aton(ip_addr,&sin.sin_addr.s_addr);
264 inet_aton(ip_addr, &sin.sin_addr);
265 strncpy(ifr.ifr_name, tunName, IFNAMSIZ);
266
267 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
268 fprintf(stderr, "ifdown: shutdown ");
269 perror(ifr.ifr_name);
270 return -1;
271 }
272
273#ifdef ifr_flags
274 #define IRFFLAGS ifr_flags
275#else /* Present on kFreeBSD */
276 #define IRFFLAGS ifr_flagshigh
277#endif
278
279 if (!(ifr.IRFFLAGS & IFF_UP)) {
280 // fprintf(stdout, "Device is currently down..setting up.-- %u\n", ifr.IRFFLAGS);
281 ifr.IRFFLAGS |= IFF_UP;
282 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
283 fprintf(stderr, "ifup: failed ");
284 perror(ifr.ifr_name);
285 return -1;
286 }
287 }
288
289 memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
290
291 // Set interface address
292 if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
293 fprintf(stderr, "Cannot set IP address. ");
294 perror(ifr.ifr_name);
295 return -1;
296 }
297
298 inet_aton(mask, &sin.sin_addr);
299 memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
300
301 if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
302 fprintf(stderr, "Cannot define subnet mask for this device");
303 perror(ifr.ifr_name);
304 return -1;
305 }
306
307#undef IRFFLAGS
308 return 0;
309}
310
311/***************************************************************************************/
312
313template<class mesh_t, class network_t, class radio_t>
315{
316 // No longer required
317}
318
319/***************************************************************************************/
320
321template<class mesh_t, class network_t, class radio_t>
323{
324
325 if (interrupts) {
326 gotInterrupt = true;
327 }
328 else {
329 handleRadioIn();
330 handleTX();
331 handleRX();
332 handleRadioOut();
333 }
334}
335
336/***************************************************************************************/
337
338template<class mesh_t, class network_t, class radio_t>
340{
341
342 if (gotInterrupt) {
343 gotInterrupt = false;
344 handleRadioIn();
345 handleTX();
346 }
347 else if (radio.rxFifoFull()) {
348 fifoCleared = true;
349 handleRadioIn();
350 handleRadioOut();
351 }
352 else {
353 delay(waitDelay);
354 }
355 handleRX();
356 handleRadioOut();
357}
358
359/***************************************************************************************/
360
361template<class mesh_t, class network_t, class radio_t>
363{
364 if (mesh_enabled) {
365 while (mesh.update()) {
366 if (!thisNodeAddress) {
367 mesh.DHCP();
368 }
369 }
370 }
371 else {
372 while (network.update()) {
373 }
374 }
375
376 RF24NetworkFrame f;
377 while (network.external_queue.size() > 0) {
378 f = network.external_queue.front();
379
380 msgStruct msg;
381
382 unsigned int bytesRead = f.message_size;
383
384 if (bytesRead > 0) {
385 memcpy(&msg.message, &f.message_buffer, bytesRead);
386 msg.size = bytesRead;
387
388#if (DEBUG_LEVEL >= 1)
389 std::cout << "Radio: Received " << bytesRead << " bytes ... " << std::endl;
390#endif
391#if (DEBUG_LEVEL >= 3)
392 // printPayload(msg.getPayloadStr(),"radio RX");
393 std::cout << "TunRead: " << std::endl;
394 for (size_t i = 0; i < msg.size; i++) {
395 // std::cout << std::hex << buffer[i];
396 printf(":%0x :", msg.message[i]);
397 }
398 std::cout << std::endl;
399
400#endif
401
402 rxQueue.push(msg);
403 }
404 else {
405 // std::cerr << "Radio: Error reading data from radio. Read '" << bytesRead << "' Bytes." << std::endl;
406 }
407 network.external_queue.pop();
408 }
409}
410
411/***************************************************************************************/
412
413template<class mesh_t, class network_t, class radio_t>
414struct in_addr ESBGateway<mesh_t, network_t, radio_t>::getLocalIP()
415{
416 struct ifaddrs *ifap, *ifa;
417 int family, s, n;
418 char host[NI_MAXHOST];
419 struct in_addr myNet;
420
421 getifaddrs(&ifap);
422 for (ifa = ifap, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
423 if (std::string("tun_nrf24").compare(ifa->ifa_name) != 0 || ifa->ifa_addr == NULL) {
424 if (ifa->ifa_next == NULL) {
425 break;
426 }
427 else {
428 continue;
429 }
430 }
431
432 family = ifa->ifa_addr->sa_family;
433
434 // This is an IPv4 interface, get the IP
435 if (family == AF_INET) {
436 s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
437 if (s == 0) {
438 myNet.s_addr = ntohl(inet_network(host));
439 freeifaddrs(ifap);
440 return myNet;
441 }
442 }
443 }
444 freeifaddrs(ifap);
445 return myNet;
446}
447
448/***************************************************************************************/
449
450template<class mesh_t, class network_t, class radio_t>
452{
453 bool ok = 0;
454
455 while (!txQueue.empty() && network.external_queue.size() == 0) {
456
457 msgStruct* msgTx = &txQueue.front();
458
459#if (DEBUG_LEVEL >= 1)
460 std::cout << "Radio: Sending " << msgTx->size << " bytes ... ";
461 std::cout << std::endl;
462#endif
463#if (DEBUG_LEVEL >= 3)
464
465 // PrintDebug == 1 does not have an endline.
466 // printPayload(msg.getPayloadStr(),"radio TX");
467#endif
468
469 std::uint8_t* tmp = msgTx->message;
470
471 if (!config_TUN) { // TAP can use RF24Mesh for address assignment, but will still use ARP for address resolution
472
473 uint32_t RF24_STR = 0x34324652; // Identifies the mac as an RF24 mac
474 uint32_t ARP_BC = 0xFFFFFFFF; // Broadcast address
475 struct macStruct
476 {
477 uint32_t rf24_Verification;
478 uint16_t rf24_Addr;
479 };
480
481 macStruct macData;
482 memcpy(&macData.rf24_Addr, tmp + 4, 2);
483 memcpy(&macData.rf24_Verification, tmp, 4);
484
485 if (macData.rf24_Verification == RF24_STR) {
486 const uint16_t other_node = macData.rf24_Addr;
487 RF24NetworkHeader header(/*to node*/ other_node, EXTERNAL_DATA_TYPE);
488 ok = network.write(header, &msgTx->message, msgTx->size);
489 }
490 else if (macData.rf24_Verification == ARP_BC) {
491 RF24NetworkHeader header(/*to node*/ 00, EXTERNAL_DATA_TYPE); // Set to master node, will be modified by RF24Network if multi-casting
492 if (msgTx->size <= 42) {
493 if (thisNodeAddress == 00) { // Master Node
494
495 uint32_t arp_timeout = millis();
496
497 ok = network.multicast(header, &msgTx->message, msgTx->size, 1); // Send to Level 1
498 while (millis() - arp_timeout < 5) {
499 network.update();
500 }
501 network.multicast(header, &msgTx->message, msgTx->size, 1); // Send to Level 1
502 arp_timeout = millis();
503 while (millis() - arp_timeout < 15) {
504 network.update();
505 }
506 network.multicast(header, &msgTx->message, msgTx->size, 1); // Send to Level 1
507 }
508 else {
509 ok = network.write(header, &msgTx->message, msgTx->size);
510 }
511 }
512 }
513 }
514 else { // TUN always needs to use RF24Mesh for address assignment AND resolution
515
516 uint8_t lastOctet = tmp[19];
517 int16_t meshAddr;
518
519 RF24NetworkHeader header(00, EXTERNAL_DATA_TYPE);
520 bool sendData = false;
521
522 struct in_addr ipDestination;
523 memcpy(&ipDestination.s_addr, &tmp[16], 4);
524
525 if ((getLocalIP().s_addr & 0x00FFFFFF) == (ipDestination.s_addr & 0x00FFFFFF)) { // Is inside the RF24Mesh network
526 if ((meshAddr = mesh.getAddress(lastOctet)) > 0) {
527 header.to_node = meshAddr;
528 sendData = true;
529 }
530 else {
531 if (thisNodeID > 0) { // If IP is in mesh range, address lookup fails, and this is not master,
532 sendData = true; // send to 00 anyway in case destination is master, or the lookup just failed
533 }
534 // printf("Could not find matching mesh nodeID for IP ending in %d\n",lastOctet);
535 }
536 }
537 else if (thisNodeID > 0) { // If not master, send to master for routing etc. if target not within mesh
538 sendData = true;
539 }
540 else if (routingTableSize > 0) {
541 for (int i = 0; i < routingTableSize; i++) {
542 struct in_addr network;
543 network.s_addr = routingStruct[i].ip.s_addr & routingStruct[i].mask.s_addr;
544 struct in_addr destNet;
545 destNet.s_addr = ipDestination.s_addr & routingStruct[i].mask.s_addr;
546 // printf("network %s destNet: %s\n",inet_ntoa(network),inet_ntoa(destNet));
547 if (destNet.s_addr == network.s_addr) {
548 uint8_t toNode = routingStruct[i].gw.s_addr >> 24;
549 int16_t netAddr = 0;
550 if ((netAddr = mesh.getAddress(toNode)) > 0) {
551 header.to_node = netAddr;
552 sendData = true;
553 break;
554 }
555 }
556 }
557 }
558
559 if (sendData) {
560 ok = network.write(header, msgTx->message, msgTx->size);
561 // std::cout << "SendData " << header.to_node << std::endl;
562 }
563 }
564 // delay( rf24_min(msgTx->size/48,20));
565 txQueue.pop();
566
567 // printf("Addr: 0%#x\n",macData.rf24_Addr);
568 // printf("Verif: 0%#x\n",macData.rf24_Verification);
569 if (ok) {
570 // std::cout << "ok." << std::endl;
571 }
572 else {
573 // std::cerr << "failed." << std::endl;
574 }
575
576 } // End Tx
577}
578
579/***************************************************************************************/
580
581template<class mesh_t, class network_t, class radio_t>
583{
584 fd_set socketSet;
585 struct timeval selectTimeout;
586 uint8_t buffer[MAX_PAYLOAD_SIZE];
587 int nread;
588
589 FD_ZERO(&socketSet);
590 FD_SET(tunFd, &socketSet);
591
592 selectTimeout.tv_sec = 0;
593 selectTimeout.tv_usec = waitDelay * 1000;
594
595 if (select(tunFd + 1, &socketSet, NULL, NULL, &selectTimeout) != 0) {
596 if (FD_ISSET(tunFd, &socketSet)) {
597 if ((nread = read(tunFd, buffer, MAX_PAYLOAD_SIZE)) >= 0) {
598
599#if (DEBUG_LEVEL >= 1)
600 std::cout << "Tun: Successfully read " << nread << " bytes from tun device" << std::endl;
601#endif
602#if (DEBUG_LEVEL >= 3)
603 std::cout << "TunRead: " << std::endl;
604 for (int i = 0; i < nread; i++)
605 {
606 printf(":%0x :", buffer[i]);
607 }
608 std::cout << std::endl;
609#endif
610 msgStruct msg;
611 memcpy(&msg.message, &buffer, nread);
612 msg.size = nread;
613 if (txQueue.size() < 10) {
614 txQueue.push(msg);
615 }
616 else {
617 droppedIncoming++;
618 }
619 }
620 else {
621#if (DEBUG_LEVEL >= 1)
622 std::cerr << "Tun: Error while reading from tun/tap interface." << std::endl;
623#endif
624 }
625 }
626 }
627}
628
629/***************************************************************************************/
630
631template<class mesh_t, class network_t, class radio_t>
633{
634
635 if (rxQueue.size() < 1)
636 {
637 return;
638 }
639 msgStruct* msg = &rxQueue.front();
640
641 if (msg->size > MAX_PAYLOAD_SIZE)
642 {
643 // printf("*****WTF OVER *****");
644 rxQueue.pop();
645 return;
646 }
647
648 if (msg->size > 0)
649 {
650
651 size_t writtenBytes = write(tunFd, &msg->message, msg->size);
652 if (writtenBytes != msg->size)
653 {
654// std::cerr << "Tun: Less bytes written to tun/tap device then requested." << std::endl;
655#if DEBUG_LEVEL >= 1
656 printf("Tun: Less bytes written %d to tun/tap device then requested %d.", writtenBytes, msg->size);
657#endif
658 }
659 else
660 {
661#if (DEBUG_LEVEL >= 1)
662 std::cout << "Tun: Successfully wrote " << writtenBytes << " bytes to tun device" << std::endl;
663#endif
664 }
665
666#if (DEBUG_LEVEL >= 3)
667 // printPayload(msg.message,"tun write");
668 std::cout << "TunRead: " << std::endl;
669 for (size_t i = 0; i < msg->size; i++) {
670 // printf(":%0x :",msg->message[i]);
671 }
672 std::cout << std::endl;
673#endif
674 }
675
676 rxQueue.pop();
677}
678
679/***************************************************************************************
680
681template<class mesh_t, class network_t, class radio_t>
682void ESBGateway<mesh_t, network_t, radio_t>::printPayload(std::string buffer, std::string debugMsg)
683{
684}
685
686/***************************************************************************************
687
688template<class mesh_t, class network_t, class radio_t>
689void ESBGateway<mesh_t, network_t, radio_t>::printPayload(char* buffer, int nread, std::string debugMsg)
690{
691}
692
693/***************************************************************************************/
694
695template<class mesh_t, class network_t, class radio_t>
697{
698 int ret;
699 const char* myAddr = "127.0.0.1";
700
701 addr.sin_family = AF_INET;
702 ret = inet_aton(myAddr, &addr.sin_addr);
703 if (ret == 0) {
704 perror("inet_aton");
705 exit(1);
706 }
707 addr.sin_port = htons(32001);
708 // buf = "Hello UDP";
709 s = socket(PF_INET, SOCK_DGRAM, 0);
710 if (s == -1) {
711 perror("socket");
712 exit(1);
713 }
714}
715
716/***************************************************************************************/
717
718template<class mesh_t, class network_t, class radio_t>
719void ESBGateway<mesh_t, network_t, radio_t>::sendUDP(uint8_t nodeID, RF24NetworkFrame frame)
720{
721
722 uint8_t buffer[MAX_PAYLOAD_SIZE + 11];
723
724 memcpy(&buffer[0], &nodeID, 1);
725 memcpy(&buffer[1], &frame.header, 8);
726 memcpy(&buffer[9], &frame.message_size, 2);
727 memcpy(&buffer[11], &frame.message_buffer, frame.message_size);
728
729 int ret = sendto(s, &buffer, frame.message_size + 11, 0, (struct sockaddr*)&addr, sizeof(addr));
730 if (ret == -1)
731 {
732 perror("sendto");
733 exit(1);
734 }
735}
736
737// ensure the compiler is aware of the possible datatype for the template class
738template class ESBGateway<ESBMesh<ESBNetwork<RF24>, RF24>, ESBNetwork<RF24>, RF24>;
bool meshEnabled()
int setIP(char *ip_addr, char *mask)
void interrupts(bool enable=1)
void poll(uint32_t waitDelay=3)
ESBGateway(radio_t &_radio, network_t &_network, mesh_t &_mesh)
void begin(uint8_t nodeID=0, uint8_t channel=97, rf24_datarate_e data_rate=RF24_1MBPS)
void update(bool interrupts=0)
void sendUDP(uint8_t nodeID, RF24NetworkFrame frame)