From ad40fcb5c48694a701d82d143f4ca6a3fae1acee Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 24 Jun 2014 03:13:09 -0600 Subject: [PATCH 001/105] Development for planned changes Initial code changes are a bit of a mess, but working OK currently. Will require reconfiguration of all network nodes. Main Changes: 1. Changed addressing to use a better default mask (CCCCCCCCCC) as the manufacturer recommends against the previous mask (F0) since it has only 1 transition, and modified the addressing function. 2. Also changed default address translation: Uses 0xc3,0x3c,0x33,0xce,0x3e,0xe3 to represent the octal numbers 0 through 5 in the address format and pipe numbers. ie: base address (pipe 0) 00 = CC:CC:CC:CC:C3 address 01 = CC:CC:CC:3C:C3 011 = CC:CC:CC:3C:3C:C3 3. New routing protocol using Dynamic Radio ACKs & Network ACK payloads: The old functionality would allow an auto-radio ACK from a routing node, but after that it was unknown whether the payload was delivered. New: Payloads between direct child and parent nodes will continue to use the radio chip built-in retry methods. Routed payloads will follow the following protocol: a: A payload is transmitted from node 011 to node 00. Node 01 must route the payload on behalf of 011. b: Node 011 sends the payload with the NoACK flag set to 01. c: If received, node 01 will forward the payload to 00 and request a radio auto-ACK. d: If the payload was delivered, 01 will forward a manual Network ACK back to 011. e: Node 011 can retry transmission as required if failed, or send another if OK. The above greatly reduces traffic for routed messages, and provides a true indication of success or failure. Testing seems to confirm benefits from the addressing changes, and the multicast routing seems to potentially increase both throughput and potential reliability as well. --- RF24Network.cpp | 190 ++++++++++++++++++------- RF24Network.h | 50 ++++--- RF24Network_config.h | 3 +- RPi/RF24Network/RF24Network.cpp | 199 +++++++++++++++++++-------- RPi/RF24Network/RF24Network.h | 10 +- RPi/RF24Network/RF24Network_config.h | 5 +- 6 files changed, 324 insertions(+), 133 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 577c9999..47c6d7d1 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -20,7 +20,13 @@ uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); bool is_valid_address( uint16_t node ); +uint32_t nFails = 0, nOK=0; +uint8_t addrLen = 0; +/*uint8_t errBuffer[32],errPipe; +uint16_t errNode; +bool errRetry = 0; +*/ /******************************************************************/ #if !defined (DUAL_HEAD_RADIO) RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) @@ -48,12 +54,15 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) radio.setChannel(_channel); radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); + radio.enableDynamicAck(); // Use different retry periods to reduce data collisions - uint8_t retryVar = (node_address % 7) + 5; + uint8_t retryVar = ((node_address % 6) *2) + 3; radio.setRetries(retryVar, 15); - txTimeout = retryVar * 17; + txTimeout = 100; + routeTimeout = 200; + printf("Retries: %d, txTimeout: %d",retryVar,txTimeout); #if defined (DUAL_HEAD_RADIO) radio1.setChannel(_channel); radio1.setDataRate(RF24_1MBPS); @@ -66,24 +75,29 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) // Open up all listening pipes int i = 6; - while (i--) - radio.openReadingPipe(i,pipe_address(_node_address,i)); + while (i--){ + radio.openReadingPipe(i,pipe_address(_node_address,i)); + } radio.startListening(); } /******************************************************************/ +void RF24Network::failures(uint32_t *_fails, uint32_t *_ok){ + *_fails = nFails; + *_ok = nOK; +} -void RF24Network::update(void) +uint8_t RF24Network::update(void) { // if there is data ready uint8_t pipe_num; - while ( radio.isValid() && radio.available(&pipe_num) ) + while ( radio.isValid() && radio.available())//&pipe_num) ) { // Dump the payloads until we've gotten everything - while (radio.available()) - { + //while (radio.available()) + //{ // Fetch the payload, and see if this was the last one. radio.read( frame_buffer, sizeof(frame_buffer) ); @@ -97,15 +111,26 @@ void RF24Network::update(void) if ( !is_valid_address(header.to_node) ){ continue; } - + + uint8_t res = header.reserved; // Is this for us? if ( header.to_node == node_address ){ - // Add it to the buffer of frames for us + if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. + #ifdef DEBUG_ROUTING + printf_P(PSTR("MAC: Network ACK Rcvd")); + #endif + return NETWORK_ACK; + } + + // Add it to the buffer of frames for us enqueue(); - }else{ - // Relay it - write(header.to_node); + + + }else{ + write(header.to_node,1); //Send it on, indicate it is a routed payload } + + // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If this was for us, from one of our children, but on our listening @@ -121,8 +146,10 @@ void RF24Network::update(void) radio.openReadingPipe(1,pipe_address(node_address,1)); } #endif - } + //} } + + return 0; } /******************************************************************/ @@ -214,7 +241,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le { // Fill out the header header.from_node = node_address; - + // Build the full frame to send memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); if (len) @@ -232,13 +259,15 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // Just queue it in the received queue return enqueue(); else - // Otherwise send it out over the air - return write(header.to_node); + // Otherwise send it out over the air + return write(header.to_node,0); + + } /******************************************************************/ -bool RF24Network::write(uint16_t to_node) +bool RF24Network::write(uint16_t to_node, bool routed) { bool ok = false; @@ -250,7 +279,8 @@ bool RF24Network::write(uint16_t to_node) //radio.stopListening(); // Where do we send this? By default, to our parent - uint16_t send_node = parent_node; + uint16_t send_node = parent_node; + // On which pipe uint8_t send_pipe = parent_pipe; @@ -271,24 +301,55 @@ bool RF24Network::write(uint16_t to_node) send_node = direct_child_route_to(to_node); send_pipe = 0; } - + bool multicast = 0; // Network ACK requested + if(!routed && send_node != to_node ){ + frame_buffer[7] = NETWORK_ACK_REQUEST; + #ifdef SERIAL_DEBUG_ROUTING + printf("Req Net Ack\n"); + #endif + } + if(send_node != to_node){ + multicast = 1; //No ACK requested ( Use manual network ACK ) + } + + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); - + #if !defined (DUAL_HEAD_RADIO) // First, stop listening so we can talk radio.stopListening(); #endif - ok = write_to_pipe( send_node, send_pipe ); - + ok=write_to_pipe(send_node, send_pipe, multicast); + + #ifdef SERIAL_DEBUG_ROUTING + if(ok == 0){ + printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); + } + #endif + if(routed && ok && send_node == to_node && frame_buffer[7] != NETWORK_ACK){ + uint16_t from = frame_buffer[0] | (frame_buffer[1] << 8) ; + frame_buffer[7] = NETWORK_ACK; + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; + write(from,1); + #if defined (SERIAL_DEBUG_ROUTING) + printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,from); + #endif + } + + + // if(!ok){ printf_P(PSTR("%lu: MAC No Ack from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } + + + //printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If we are talking on our talking pipe, it's possible that no one is listening. // If this fails, try sending it on our parent's listening pipe. That will wake // it up, and next time it will listen to us. - if ( !ok && send_node == parent_node ) - ok = write_to_pipe( parent_node, 0 ); + //if ( !ok && send_node == parent_node ) + // ok = write_to_pipe( parent_node, 0 ); #endif #if !defined (DUAL_HEAD_RADIO) @@ -296,31 +357,60 @@ bool RF24Network::write(uint16_t to_node) radio.startListening(); #endif + if(send_node != to_node && !routed){ + uint32_t reply_time = millis(); + while( update() != NETWORK_ACK){ + if(millis() - reply_time > routeTimeout){ + #if def SERIAL_DEBUG_ROUTING + printf_P(PSTR("%lu: MAC Network ACK fail from 0%o on pipe %x\n\r"),millis(),to_node,send_pipe); + #endif + ok=0; + break; + } + } + } + + if(ok == true){ + nOK++; + }else{ nFails++; + } return ok; } /******************************************************************/ -bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe ) + +bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { bool ok = false; - + uint64_t out_pipe = pipe_address( node, pipe ); #if !defined (DUAL_HEAD_RADIO) // Open the correct pipe for writing. - radio.openWritingPipe(out_pipe); + radio.openWritingPipe(out_pipe); - radio.writeFast(frame_buffer, frame_size); - ok = radio.txStandBy(txTimeout); + radio.writeFast(frame_buffer, frame_size,multicast); + ok = radio.txStandBy(txTimeout); + #else radio1.openWritingPipe(out_pipe); radio1.writeFast(frame_buffer, frame_size); - ok = radio1.txStandBy(txTimeout); + ok = radio1.txStandBy(txTimeout,multicast); #endif + #ifdef __arm__ + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %lx %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); + #else IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %lx %S\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); + #endif + + /*for(int i=0; i<5; i++){ + uint8_t tmp = out_pipe; + out_pipe=out_pipe >> 8; + Serial.println(tmp,HEX); + }*/ return ok; } @@ -391,6 +481,7 @@ void RF24Network::setup_address(void) m >>= 3; } parent_pipe = i; + //parent_pipe = i-1; #ifdef SERIAL_DEBUG printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe); @@ -448,27 +539,26 @@ bool is_valid_address( uint16_t node ) uint64_t pipe_address( uint16_t node, uint8_t pipe ) { - static uint8_t pipe_segment[] = { 0x3c, 0x5a, 0x69, 0x96, 0xa5, 0xc3 }; - - uint64_t result; + + static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3 }; + uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); - - out[0] = pipe_segment[pipe]; - - uint8_t w; - short i = 4; - short shift = 12; - while(i--) - { - w = ( node >> shift ) & 0xF ; - w |= ~w << 4; - out[i+1] = w; - - shift -= 4; - } - + + // Translate the address to use our optimally chosen radio address bytes + uint8_t count = 0; uint16_t dec = node; + while(dec){ + out[count]=address_translation[dec % 8]; // Convert our decimal values to octal, translate them to address bytes, and set our address + dec /= 8; + count++; + } + + //if( pipe > 0 ){ + result = result << 8; // Shift the result by 1 byte + out[0] = address_translation[pipe]; // Set last byte by pipe number + //} + IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%lu: NET Pipe %i on node 0%o has address %lx%x\n\r"),millis(),pipe,node,*top,*out)); - + return result; } diff --git a/RF24Network.h b/RF24Network.h index d65d75c0..148a58df 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -31,7 +31,7 @@ struct RF24NetworkHeader 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 */ + unsigned char reserved; /**< Reserved for routing messages */ static uint16_t next_id; /**< The message ID of the next message to be sent */ @@ -99,14 +99,15 @@ class RF24Network * @param _node_address The logical address of this node */ void begin(uint8_t _channel, uint16_t _node_address ); - + + void failures(uint32_t *_fails, uint32_t *_ok); /** * Main layer loop * * This function must be called regularly to keep the layer going. This is where all * the action happens! */ - void update(void); + uint8_t update(void); /** * Test whether there is a message available for this node @@ -225,13 +226,24 @@ class RF24Network * */ - unsigned long txTimeout; + uint32_t txTimeout; + + /** + * @note: Optimization: This new value defaults to 200 milliseconds. + * This only affects payloads that are routed by one or more nodes. + * This specifies how long to wait for an ack from across the network. + * Radios routing directly to their parent or children nodes do not + * utilize this value. + */ + + uint16_t routeTimeout; + 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 write(uint16_t,bool routed); + bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); bool enqueue(void); bool is_direct_child( uint16_t node ); @@ -251,11 +263,14 @@ class RF24Network 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 */ - + uint8_t error_frame[frame_size]; + 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 */ - + #define NETWORK_ACK_REQUEST 128 + #define NETWORK_ACK 129 + }; /** @@ -458,6 +473,8 @@ class RF24Network * @page Tuning Performance and Data Loss: Tuning the Network * Tips and examples for tuning the network and Dual-head operation. * + * Topology + * * @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 @@ -482,15 +499,14 @@ class RF24Network * 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. + * example in the above section. 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 timeout periods by default, but + * they can also be adjusted on a per-node basis. + * + * The txTimeout variable is used to extend the retry count to a defined duration in milliseconds. 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 * diff --git a/RF24Network_config.h b/RF24Network_config.h index 0f399967..741bb959 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -52,7 +52,8 @@ #undef SERIAL_DEBUG - #ifdef SERIAL_DEBUG + #define SERIAL_DEBUG_ROUTING + #if defined (SERIAL_DEBUG) #define IF_SERIAL_DEBUG(x) ({x;}) #else #define IF_SERIAL_DEBUG(x) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index df116a7e..ffffbe29 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -14,16 +14,17 @@ #include #include #include -#include +#include #include #include "RF24Network_config.h" -#include "../RF24/RF24.h" +#include #include "RF24Network.h" uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); bool is_valid_address( uint16_t node ); +uint32_t nFails = 0, nOK=0; /******************************************************************/ @@ -35,38 +36,47 @@ RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { - if (! is_valid_address(_node_address) ) + if (! is_valid_address(_node_address) ){ return; - +} + node_address = _node_address; - + if ( ! radio.isValid() ){ return; } - + // Set up the radio the way we want it to look radio.setChannel(_channel); radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); + radio.enableDynamicAck(); - uint8_t retryVar = (node_address % 7) + 5; + //uint8_t retryVar = (node_address % 7) + 5; + uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 15); txTimeout = retryVar * 17; - + // Setup our address helper cache setup_address(); - + // Open up all listening pipes int i = 6; - while (i--) + while (i--){ radio.openReadingPipe(i,pipe_address(_node_address,i)); + } + //radio.setAutoAck(5,0); radio.startListening(); } /******************************************************************/ +void RF24Network::failures(uint32_t *_fails, uint32_t *_ok){ + *_fails = nFails; + *_ok = nOK; +} -void RF24Network::update(void) +uint8_t RF24Network::update(void) { // if there is data ready uint8_t pipe_num; @@ -74,30 +84,38 @@ void RF24Network::update(void) { // Dump the payloads until we've gotten everything - while (radio.available()) - { + //while (radio.available()) + //{ // Fetch the payload, and see if this was the last one. radio.read( frame_buffer, sizeof(frame_buffer) ); // Read the beginning of the frame as the header const RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Received on %u %s\n\r"),millis(),pipe_num,header.toString())); - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); + IF_SERIAL_DEBUG(printf_P("%d: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%d: NET message %04x\n\r",millis(),*i)); // Throw it away if it's not a valid address if ( !is_valid_address(header.to_node) ){ continue; } + + + uint8_t res = header.reserved; // Is this for us? if ( header.to_node == node_address ){ - // Add it to the buffer of frames for us - enqueue(); + if(res == NETWORK_ACK){ + return NETWORK_ACK; + } + + // Add it to the buffer of frames for us + enqueue(); + }else{ - // Relay it - write(header.to_node); + // Relay it as a routed message + write(header.to_node,1); } - + // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If this was for us, from one of our children, but on our listening @@ -113,30 +131,34 @@ void RF24Network::update(void) radio.openReadingPipe(1,pipe_address(node_address,1)); } #endif - } + //} } + return 0; } + + + /******************************************************************/ bool RF24Network::enqueue(void) { bool result = false; - - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Enqueue @%x "),millis(),next_frame-frame_queue)); + + IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Enqueue @%x "),millis(),next_frame-frame_queue)); // Copy the current frame into the frame queue if ( next_frame < frame_queue + sizeof(frame_queue) ) { memcpy(next_frame,frame_buffer, frame_size ); - next_frame += frame_size; + next_frame += frame_size; result = true; - IF_SERIAL_DEBUG(printf_P(PSTR("ok\n\r"))); + IF_SERIAL_DEBUG(printf("ok\n\r")); } else { - IF_SERIAL_DEBUG(printf_P(PSTR("failed\n\r"))); + IF_SERIAL_DEBUG(printf("failed\n\r")); } return result; @@ -179,18 +201,18 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { - // Move the pointer back one in the queue + // Move the pointer back one in the queue next_frame -= frame_size; uint8_t* frame = next_frame; - + // How much buffer size should we actually copy? bufsize = std::min(maxlen,frame_size-sizeof(RF24NetworkHeader)); // Copy the next available frame from the queue into the provided buffer memcpy(&header,frame,sizeof(RF24NetworkHeader)); memcpy(message,frame+sizeof(RF24NetworkHeader),bufsize); - - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); + + IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Received %s\n\r"),millis(),header.toString())); } return bufsize; @@ -208,10 +230,10 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (len) memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Sending %s\n\r"),millis(),header.toString())); + IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Sending %s\n\r"),millis(),header.toString())); if (len) { - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%d: NET message %04x\n\r"),millis(),*i)); } @@ -221,15 +243,15 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le return enqueue(); else // Otherwise send it out over the air - return write(header.to_node); + return write(header.to_node,0); } /******************************************************************/ -bool RF24Network::write(uint16_t to_node) +bool RF24Network::write(uint16_t to_node, bool routed) { bool ok = false; - + // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) return false; @@ -241,7 +263,7 @@ bool RF24Network::write(uint16_t to_node) uint16_t send_node = parent_node; // On which pipe uint8_t send_pipe = parent_pipe; - + // If the node is a direct child, if ( is_direct_child(to_node) ) { @@ -260,15 +282,38 @@ bool RF24Network::write(uint16_t to_node) send_pipe = 0; } - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); + bool multicast = 0; + if(!routed && send_node != to_node ){ // If sending original message, and message needs to be routed + frame_buffer[7] = NETWORK_ACK_REQUEST; // Request a manual ACK payload + multicast = 1; // Send via multicast since message is routed, a manual network-ACK will be sent if successful + } + if(send_node != to_node){ + multicast = 1; //No ACK requested + } + IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); // First, stop listening so we can talk radio.stopListening(); - + // Put the frame on the pipe - ok = write_to_pipe( send_node, send_pipe ); - - // NOT NEEDED anymore. Now all reading pipes are open to start. + ok = write_to_pipe( send_node, send_pipe, multicast ); + + + if(routed && ok && send_node == to_node && frame_buffer[7] != NETWORK_ACK){ + uint16_t from = frame_buffer[0] | (frame_buffer[1] << 8) ; + frame_buffer[7] = NETWORK_ACK; + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; + write(from,1); + #if defined (SERIAL_DEBUG_ROUTING) + printf("NET: ACK to %o \n",from); + #endif + } + + #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) + if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } + #endif + + // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If we are talking on our talking pipe, it's possible that no one is listening. // If this fails, try sending it on our parent's listening pipe. That will wake @@ -281,25 +326,44 @@ bool RF24Network::write(uint16_t to_node) // Now, continue listening radio.startListening(); - return ok; + if(send_node != to_node && !routed && ok){ + uint32_t reply_time = millis(); + //bool pOK = 1; + while( update() != NETWORK_ACK){ + if(millis() - reply_time > 500){ + ok=0; + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); + #endif + break; + } + } + //if(pOK){printf("pOK\n");} + } + if(ok == true){ + nOK++; + }else{ nFails++; + } +return ok; } /******************************************************************/ -bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe ) +bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { bool ok = false; - + uint64_t out_pipe = pipe_address( node, pipe ); - - // Open the correct pipe for writing. + + // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); // Retry a few times - radio.writeFast(frame_buffer, frame_size); - ok = radio.txStandBy(txTimeout); - - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %lx %S\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); + radio.writeFast(frame_buffer, frame_size, multicast); + ok = radio.txStandBy(); + //ok = radio.write(frame_buffer,frame_size); + + IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); return ok; } @@ -351,7 +415,7 @@ void RF24Network::setup_address(void) uint16_t node_mask_check = 0xFFFF; while ( node_address & node_mask_check ) node_mask_check <<= 3; - + node_mask = ~ node_mask_check; // parent mask is the next level down @@ -367,8 +431,9 @@ void RF24Network::setup_address(void) { i >>= 3; m >>= 3; - } + } parent_pipe = i; + //parent_pipe=i-1; #ifdef SERIAL_DEBUG printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe); @@ -391,7 +456,7 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { uint16_t i = node; uint16_t m = node_mask; - + while (m) { i >>= 3; @@ -426,26 +491,40 @@ bool is_valid_address( uint16_t node ) uint64_t pipe_address( uint16_t node, uint8_t pipe ) { - static uint8_t pipe_segment[] = { 0x3c, 0x5a, 0x69, 0x96, 0xa5, 0xc3 }; - uint64_t result; + + static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3 }; + uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); - + + // Translate the address to use our optimally chosen radio address bytes + uint8_t count = 0; uint16_t dec = node; + while(dec){ + out[count]=address_translation[dec % 8]; // Convert our decimal values to octal, translate them to address bytes, and set our address + dec /= 8; + count++; + } + + //if( pipe > 0 ){ + result = result << 8; // Shift the result by 1 byte + out[0] = address_translation[pipe]; // Set last byte by pipe number + //} + /* out[0] = pipe_segment[pipe]; - uint8_t w; + uint8_t w; short i = 4; short shift = 12; while(i--) { - w = ( node >> shift ) & 0xF ; + w = ( node >> shift ) & 0xF ; w |= ~w << 4; out[i+1] = w; shift -= 4; } - - IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%lu: NET Pipe %i on node 0%o has address %lx%x\n\r"),millis(),pipe,node,*top,*out)); +*/ + IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%d: NET Pipe %i on node 0%o has address %x%x\n\r"),millis(),pipe,node,*top,*out)); return result; } diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 110f7868..a2afd51d 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -102,13 +102,15 @@ class RF24Network */ void begin(uint8_t _channel, uint16_t _node_address ); + void failures(uint32_t *_fails, uint32_t *_ok); + /** * Main layer loop * * This function must be called regularly to keep the layer going. This is where all * the action happens! */ - void update(void); + uint8_t update(void); /** * Test whether there is a message available for this node @@ -174,8 +176,8 @@ class RF24Network protected: 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 write(uint16_t, bool routed); + bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); bool enqueue(void); bool is_direct_child( uint16_t node ); @@ -195,6 +197,8 @@ class RF24Network 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 */ + #define NETWORK_ACK_REQUEST 128 + #define NETWORK_ACK 129 }; /** diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 8a1c57eb..84e6e0fd 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -20,8 +20,9 @@ #define _BV(x) (1<<(x)) #endif -#undef SERIAL_DEBUG -#ifdef SERIAL_DEBUG +#undef SERIAL_DEBUG //Change #undef to #define for debug +#define SERIAL_DEBUG_ROUTING +#if defined (SERIAL_DEBUG) #define IF_SERIAL_DEBUG(x) ({x;}) #else #define IF_SERIAL_DEBUG(x) From 247431141e3d682091e10a11fdd56f9026c92440 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 26 Jun 2014 20:54:40 -0600 Subject: [PATCH 002/105] Add Pandora's box routing - Cleaned up prev changes a bit - Added what I like to call Pandora's box routing: Nodes are able to write network payloads directly to any host, who will then route the payloads as required. If sending to the host that will receive the payload, a radio ACK will be sent directly to the sender. If sending to a host that is not the recipient, it will be forwarded to the correct host, and a network ACK will be sent by the final delivering node, The network ack will traverse the network just as any other message, and will fail if the main link to the sending node is down. This allows failover nodes to be put in place. Failover nodes should not have any children, OR should have spare pipes available. Node 014 will always write to pipe1 for pandora routing, and node 023 will always write to pipe 2, so any failover nodes should have pipes 1 and 2 available if used for both. Duplicate payloads are still an issue when network acks fail and are retried, so can be filtered out by the receiver based on the network header id number if required. --- RF24Network.cpp | 102 +++++++++++++++++--------------- RF24Network.h | 67 ++++++++++++++++++--- RPi/RF24Network/RF24Network.cpp | 85 ++++++++++++++++---------- RPi/RF24Network/RF24Network.h | 18 +++++- 4 files changed, 179 insertions(+), 93 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 47c6d7d1..b43db246 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -21,12 +21,7 @@ uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); bool is_valid_address( uint16_t node ); uint32_t nFails = 0, nOK=0; -uint8_t addrLen = 0; -/*uint8_t errBuffer[32],errPipe; -uint16_t errNode; -bool errRetry = 0; -*/ /******************************************************************/ #if !defined (DUAL_HEAD_RADIO) RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) @@ -60,9 +55,9 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) uint8_t retryVar = ((node_address % 6) *2) + 3; radio.setRetries(retryVar, 15); txTimeout = 100; - routeTimeout = 200; + routeTimeout = txTimeout+25; // About 2.5ms max delay per node at optimal routing speeds, 10 nodes maximum hop + max retry time for auto-ack - printf("Retries: %d, txTimeout: %d",retryVar,txTimeout); + printf_P(PSTR("Retries: %d, txTimeout: %d"),retryVar,txTimeout); #if defined (DUAL_HEAD_RADIO) radio1.setChannel(_channel); radio1.setDataRate(RF24_1MBPS); @@ -112,21 +107,22 @@ uint8_t RF24Network::update(void) continue; } - uint8_t res = header.reserved; + uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. - #ifdef DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd")); + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: Network ACK Rcvd\n")); #endif return NETWORK_ACK; } - + //printf("enQ\n"); // Add it to the buffer of frames for us enqueue(); - + nOK++; }else{ + //printf("route"); write(header.to_node,1); //Send it on, indicate it is a routed payload } @@ -235,9 +231,17 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) return bufsize; } +/******************************************************************/ +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ + return _write(header,message,len,070); +} +/******************************************************************/ +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ + return _write(header,message,len,writeDirect); +} /******************************************************************/ -bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len) +bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect) { // Fill out the header header.from_node = node_address; @@ -260,32 +264,42 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le return enqueue(); else // Otherwise send it out over the air - return write(header.to_node,0); - + if(writeDirect != 070){ + if(header.to_node == writeDirect){ + return write(writeDirect,2); + }else{ + return write(writeDirect,3); + } + }else{ + return write(header.to_node,0); + } } /******************************************************************/ -bool RF24Network::write(uint16_t to_node, bool routed) +bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = First Payload, standard routing, 1=routed payload, 2=directRoute to host, 3=directRoute to Route { bool ok = false; - + bool multicast = 0; // Radio ACK requested = 0 + // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) return false; - // First, stop listening so we can talk. - //radio.stopListening(); // Where do we send this? By default, to our parent uint16_t send_node = parent_node; // On which pipe uint8_t send_pipe = parent_pipe; - + + if(directTo>1){ + send_node = to_node; + } + // If the node is a direct child, - if ( is_direct_child(to_node) ) + else if ( is_direct_child(to_node) ) { // Send directly send_node = to_node; @@ -301,39 +315,26 @@ bool RF24Network::write(uint16_t to_node, bool routed) send_node = direct_child_route_to(to_node); send_pipe = 0; } - bool multicast = 0; // Network ACK requested - if(!routed && send_node != to_node ){ - frame_buffer[7] = NETWORK_ACK_REQUEST; - #ifdef SERIAL_DEBUG_ROUTING - printf("Req Net Ack\n"); - #endif - } - if(send_node != to_node){ - multicast = 1; //No ACK requested ( Use manual network ACK ) - } + + if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3){ + multicast = 1; + } + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); -#if !defined (DUAL_HEAD_RADIO) - // First, stop listening so we can talk - radio.stopListening(); -#endif - ok=write_to_pipe(send_node, send_pipe, multicast); #ifdef SERIAL_DEBUG_ROUTING - if(ok == 0){ - printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); - } + if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } #endif - if(routed && ok && send_node == to_node && frame_buffer[7] != NETWORK_ACK){ - uint16_t from = frame_buffer[0] | (frame_buffer[1] << 8) ; - frame_buffer[7] = NETWORK_ACK; - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(from,1); + if( directTo == 1 && ok && !multicast ){ + frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address + write(frame_buffer[0] | (frame_buffer[1] << 8),1); // Send it back as a routed message #if defined (SERIAL_DEBUG_ROUTING) - printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,from); + printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),to_node,frame_buffer[0] | (frame_buffer[1] << 8)); #endif } @@ -357,11 +358,11 @@ bool RF24Network::write(uint16_t to_node, bool routed) radio.startListening(); #endif - if(send_node != to_node && !routed){ + if( (send_node != to_node && directTo==0) || directTo == 3 ){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ - #if def SERIAL_DEBUG_ROUTING + #ifdef SERIAL_DEBUG_ROUTING printf_P(PSTR("%lu: MAC Network ACK fail from 0%o on pipe %x\n\r"),millis(),to_node,send_pipe); #endif ok=0; @@ -371,8 +372,9 @@ bool RF24Network::write(uint16_t to_node, bool routed) } if(ok == true){ - nOK++; + //nOK++; }else{ nFails++; + //printf("Fail to %o",to_node); } return ok; } @@ -388,8 +390,10 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) #if !defined (DUAL_HEAD_RADIO) // Open the correct pipe for writing. - radio.openWritingPipe(out_pipe); + // First, stop listening so we can talk + radio.stopListening(); + radio.openWritingPipe(out_pipe); radio.writeFast(frame_buffer, frame_size,multicast); ok = radio.txStandBy(txTimeout); diff --git a/RF24Network.h b/RF24Network.h index 148a58df..d2a53a5f 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -150,7 +150,8 @@ class RF24Network * @return Whether the message was successfully received */ bool write(RF24NetworkHeader& header,const void* message, size_t len); - + bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + /** * 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 @@ -242,7 +243,7 @@ class RF24Network private: void open_pipes(void); uint16_t find_node( uint16_t current_node, uint16_t target_node ); - bool write(uint16_t,bool routed); + bool write(uint16_t, uint8_t directTo); bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); bool enqueue(void); @@ -251,7 +252,8 @@ class RF24Network uint16_t direct_child_route_to( uint16_t node ); uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); - + bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + private: RF24& radio; /**< Underlying radio driver, provides link/physical layers */ @@ -268,9 +270,8 @@ class RF24Network 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 */ - #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 - + }; /** @@ -337,13 +338,14 @@ class RF24Network * @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 + * For many Arduino uses, they seem like overkill. So I am working to improve the current + * standard for nRF24L01 radios. The best RF24 modules are available for less than * $6 from many sources. With the RF24Network layer, I hope to cover many * common communication scenarios. * @@ -352,10 +354,12 @@ class RF24Network * @section Features Features * * The layer provides: - * @li New (2014): Dual headed operation: The use of dual radios for busy routing nodes or the master node enhances throughput and decreases errors. See the Tuning section. + * @li New (2014): Network ACKs: Efficient acknowledgement of network-wide transmissions, via dynamic radio acks and network protocol acks. + * @li New (2014): Updated addressing standard for optimal radio transmission. * @li New (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 New (2014): Optimization to the core library provides improvements to reliability, speed and efficiency. See https://tmrh20.github.io/RF24 for more info. * @li New (2014): Built in sleep mode using interrupts. (Still under development. (Enable via RF24Network_config.h)) + * @li New (2014): Dual headed operation: The use of dual radios for busy routing nodes or the master node enhances throughput and decreases errors. See the Tuning section. * @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. @@ -470,11 +474,56 @@ class RF24Network * @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. + * Tips and examples for tuning the network and general operation. * * Topology * + * @section General Understanding Radio Communication and Topology + * When a transmission takes place from one radio module to another, the receiving radio will communicate + * back to the sender with an acknowledgement (ACK) packet, to indicate success. If the sender does not + * receive an ACK, the radio automatically engages in a series of timed retries, at set intervals. The + * radios use techniques like addressing and numbering of payloads to manage this, but it is all done + * automatically, out of sight from the user. + * + * When working over a radio network, some of these automated techniques can actually hinder data transmission. + * Retrying failed payloads over and over on a radio network can hinder communication for nearby nodes, or + * reduce throughput and errors on routing nodes. + * + * Radios in this network are linked by addresses assigned to pipes. Each radio can listen + * to 6 addresses on 6 pipes, therefore each radio has a parent pipe and 5 child pipes, which are used + * to form a tree structure. Nodes communicate directly with their parent and children nodes. Any other + * traffic to or from a node must be routed through the network. + * + * @section Network Routing + * + * Routing of traffic is handled invisibly to the user. If the network is constructed appropriately, nodes + * will route traffic automatically as required. Data transmission generally has one of two requirements, + * either data that fails to transmit can be discarded as new data arrives, or sending can be retried as + * required until complete success or failure. + * + * The new routing protocol allows this to be managed at the application level as the data requires, with + * defaults assigned specifically to allow maximum efficiency and throughput from the RF level to the + * network and application level. If routing data between parent and child nodes (marked by direct links on + * the topology image above) the network uses built-in acknowledgement and retry functions of the chip to + * prevent data loss. When payloads are sent to other nodes, they need to be routed. Routing is managed using + * a combination of built in ACK requests, and software driven network ACKs. This allows all routing nodes to + * forward data very quickly, with only the final routing node confirming delivery and sending back an + * acknowledgement. + * + * Example: Node 00 sends to node 01. The nodes will use the built in auto-retry and auto-ack functions.
+ * Exmaple: Node 00 sends to node 011. Node 00 will send to node 01, and request -no radio ACK-. Node 01 will + * forward the message to 011 and request an auto radio ACK. If delivery was successful, node 01 will also + * forward a message back to node 00, (noACK) indicating success. + * + * Old Functionality: Node 00 sends to node 011 using auto-ack. Node 00 first sends to 01, 01 acknowledges. + * Node 01 forwards the payload to 011 using auto-ack. If the payload fails between 01 and 011, node 00 has + * no way of knowing. The new method uses the same amount of traffic to accomplish more. + * + * * @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 diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index ffffbe29..04297604 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -56,7 +56,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 15); txTimeout = retryVar * 17; - + routeTimeout = txTimeout+25; + // Setup our address helper cache setup_address(); @@ -101,7 +102,7 @@ uint8_t RF24Network::update(void) } - uint8_t res = header.reserved; + uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ @@ -110,7 +111,7 @@ uint8_t RF24Network::update(void) // Add it to the buffer of frames for us enqueue(); - + }else{ // Relay it as a routed message write(header.to_node,1); @@ -218,9 +219,17 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) return bufsize; } +/******************************************************************/ +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ + return _write(header,message,len,070); +} +/******************************************************************/ +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ + return _write(header,message,len,writeDirect); +} /******************************************************************/ -bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len) +bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect) { // Fill out the header header.from_node = node_address; @@ -242,16 +251,25 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // Just queue it in the received queue return enqueue(); else - // Otherwise send it out over the air - return write(header.to_node,0); + if(writeDirect != 070){ + if(header.to_node == writeDirect){ + return write(writeDirect,2); + }else{ + return write(writeDirect,3); + } + }else{ + // Otherwise send it out over the air + return write(header.to_node,0); + } } /******************************************************************/ -bool RF24Network::write(uint16_t to_node, bool routed) +bool RF24Network::write(uint16_t to_node, uint8_t directTo) { bool ok = false; - + bool multicast = 0; // Radio ACK requested = 0 + // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) return false; @@ -263,9 +281,14 @@ bool RF24Network::write(uint16_t to_node, bool routed) uint16_t send_node = parent_node; // On which pipe uint8_t send_pipe = parent_pipe; - + + if(directTo>1){ + send_node = to_node; + send_pipe = parent_pipe; + } + // If the node is a direct child, - if ( is_direct_child(to_node) ) + else if ( is_direct_child(to_node) ) { // Send directly send_node = to_node; @@ -281,37 +304,34 @@ bool RF24Network::write(uint16_t to_node, bool routed) send_node = direct_child_route_to(to_node); send_pipe = 0; } + - bool multicast = 0; - if(!routed && send_node != to_node ){ // If sending original message, and message needs to be routed - frame_buffer[7] = NETWORK_ACK_REQUEST; // Request a manual ACK payload - multicast = 1; // Send via multicast since message is routed, a manual network-ACK will be sent if successful - } - if(send_node != to_node){ - multicast = 1; //No ACK requested + if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3){ + multicast = 1; } + IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); - // First, stop listening so we can talk - radio.stopListening(); + // Put the frame on the pipe ok = write_to_pipe( send_node, send_pipe, multicast ); - +//printf("Multi %d\n",multicast); - if(routed && ok && send_node == to_node && frame_buffer[7] != NETWORK_ACK){ - uint16_t from = frame_buffer[0] | (frame_buffer[1] << 8) ; - frame_buffer[7] = NETWORK_ACK; + #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) + if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } + #endif + + if( directTo == 1 && ok && !multicast ){ + frame_buffer[6] = NETWORK_ACK; frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(from,1); + write(frame_buffer[0] | (frame_buffer[1] << 8),1); #if defined (SERIAL_DEBUG_ROUTING) - printf("NET: ACK to %o \n",from); + printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,frame_buffer[0] | (frame_buffer[1] << 8)); #endif } - #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } - #endif + // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 @@ -326,11 +346,10 @@ bool RF24Network::write(uint16_t to_node, bool routed) // Now, continue listening radio.startListening(); - if(send_node != to_node && !routed && ok){ + if( (send_node != to_node && directTo==0) || directTo == 3 ){ uint32_t reply_time = millis(); - //bool pOK = 1; while( update() != NETWORK_ACK){ - if(millis() - reply_time > 500){ + if(millis() - reply_time > routeTimeout){ ok=0; #ifdef SERIAL_DEBUG_ROUTING printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); @@ -354,7 +373,9 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) bool ok = false; uint64_t out_pipe = pipe_address( node, pipe ); - + + // First, stop listening so we can talk + radio.stopListening(); // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index a2afd51d..cfcd768f 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -153,7 +153,7 @@ class RF24Network * @return Whether the message was successfully received */ bool write(RF24NetworkHeader& header,const void* message, size_t len); - + bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); /** * This node's parent address * @@ -173,10 +173,21 @@ class RF24Network unsigned long txTimeout; + /** + * @note: Optimization: This new value defaults to 200 milliseconds. + * This only affects payloads that are routed by one or more nodes. + * This specifies how long to wait for an ack from across the network. + * Radios routing directly to their parent or children nodes do not + * utilize this value. + */ + + uint16_t routeTimeout; + + protected: void open_pipes(void); uint16_t find_node( uint16_t current_node, uint16_t target_node ); - bool write(uint16_t, bool routed); + bool write(uint16_t, uint8_t directTo); bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); bool enqueue(void); @@ -185,7 +196,8 @@ class RF24Network uint16_t direct_child_route_to( uint16_t node ); uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); - + bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + private: RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ From 6e25453ffabf583c0304b701a3cfcf8f98c9fc94 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 29 Jun 2014 12:02:44 -0600 Subject: [PATCH 003/105] Add network wide multicasting and multicast routing - Multicasting works in levels, with each node on the same level sharing a multicast address (levels 1-6) - Direct child nodes are limited to 4-per-parent in multicast mode, but a multicast node can have unlimited children if they only need to receive. If more than 4 need to transmit, they can transmit to any node other than the parent/relay node by writing directly to it with a payload addressed to another node. - Nodes can be setup as multicast relays and will always relay multicast payloads onto the next level - Any node(s) can be assigned any multicast level - A network topology can combine multicast and non-multicast branches and nodes as required, along with standard network routing - Duplicate multicast payloads are automatically filtered out, so multiple multicast relay nodes can be active in the same area eg: Send to all level 1 nodes: network.multicast(header,&message,sizeof(unsigned long),1); Multicasting is enabled by uncommenting #define RF24NetworkMulticast in the RF24Network_config.h file --- RF24Network.cpp | 178 ++++++++++++++++++++------- RF24Network.h | 41 +++++- RF24Network_config.h | 2 +- RPi/RF24Network/RF24Network.cpp | 178 ++++++++++++++++++++------- RPi/RF24Network/RF24Network.h | 44 ++++++- RPi/RF24Network/RF24Network_config.h | 6 + 6 files changed, 356 insertions(+), 93 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index b43db246..2be130f8 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -19,6 +19,9 @@ uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); +#if defined (RF24NetworkMulticast) +uint16_t levelToAddress( uint8_t level ); +#endif bool is_valid_address( uint16_t node ); uint32_t nFails = 0, nOK=0; @@ -53,11 +56,11 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) // Use different retry periods to reduce data collisions uint8_t retryVar = ((node_address % 6) *2) + 3; - radio.setRetries(retryVar, 15); - txTimeout = 100; - routeTimeout = txTimeout+25; // About 2.5ms max delay per node at optimal routing speeds, 10 nodes maximum hop + max retry time for auto-ack + radio.setRetries(retryVar, 5); + txTimeout = 20; + routeTimeout = txTimeout+35; // About 2.5ms max delay per node at optimal routing speeds, 10 nodes maximum hop + max retry time for auto-ack + some delay to relieve congestion - printf_P(PSTR("Retries: %d, txTimeout: %d"),retryVar,txTimeout); + //printf_P(PSTR("Retries: %d, txTimeout: %d"),retryVar,txTimeout); #if defined (DUAL_HEAD_RADIO) radio1.setChannel(_channel); radio1.setDataRate(RF24_1MBPS); @@ -73,6 +76,14 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) while (i--){ radio.openReadingPipe(i,pipe_address(_node_address,i)); } + #if defined (RF24NetworkMulticast) + uint8_t count = 0; + while(_node_address){ + _node_address/=8; + count++; + } + multicast_level = count; + #endif radio.startListening(); } @@ -89,6 +100,7 @@ uint8_t RF24Network::update(void) uint8_t pipe_num; while ( radio.isValid() && radio.available())//&pipe_num) ) { + // Dump the payloads until we've gotten everything //while (radio.available()) @@ -109,21 +121,44 @@ uint8_t RF24Network::update(void) uint8_t res = header.type; // Is this for us? - if ( header.to_node == node_address ){ + if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. #ifdef SERIAL_DEBUG_ROUTING printf_P(PSTR("MAC: Network ACK Rcvd\n")); #endif return NETWORK_ACK; - } - //printf("enQ\n"); - // Add it to the buffer of frames for us - enqueue(); + } // Add it to the buffer of frames for us + + enqueue(); + nOK++; }else{ - //printf("route"); - write(header.to_node,1); //Send it on, indicate it is a routed payload + + #if defined (RF24NetworkMulticast) + if( header.to_node == 0100){ + if(header.id != lastMultiMessageID){ + if(multicastRelay){ + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); + #endif + write(levelToAddress(multicast_level)<<3,4); + } + enqueue(); + lastMultiMessageID = header.id; + } + #ifdef SERIAL_DEBUG_ROUTING + else{ + printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); + } + #endif + }else{ + write(header.to_node,1); //Send it on, indicate it is a routed payload + } + #else + //if(radio.available()){printf("------FLUSHED DATA --------------");} + write(header.to_node,1); //Send it on, indicate it is a routed payload + #endif } @@ -231,6 +266,29 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) return bufsize; } +#if defined RF24NetworkMulticast +/******************************************************************/ +bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level){ + // Fill out the header + header.to_node = 0100; + header.from_node = node_address; + + // Build the full frame to send + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + if (len) + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,min(frame_size-sizeof(RF24NetworkHeader),len)); + + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Sending %s\n\r"),millis(),header.toString())); + if (len) + { + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); + } + + return write(levelToAddress(level),4); + +} +#endif + /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ return _write(header,message,len,070); @@ -292,9 +350,14 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F uint16_t send_node = parent_node; // On which pipe - uint8_t send_pipe = parent_pipe; + uint8_t send_pipe = parent_pipe %5; + + if(directTo == 4){ + send_node = to_node; + send_pipe = 0; + } - if(directTo>1){ + else if(directTo>1){ send_node = to_node; } @@ -305,7 +368,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F send_node = to_node; // To its listening pipe - send_pipe = 0; + send_pipe = 5; } // If the node is a child of a child // talk on our child's listening pipe, @@ -313,17 +376,18 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F else if ( is_descendant(to_node) ) { send_node = direct_child_route_to(to_node); - send_pipe = 0; + send_pipe = 5; } - if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3){ + if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ multicast = 1; } IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); + radio.stopListening(); ok=write_to_pipe(send_node, send_pipe, multicast); #ifdef SERIAL_DEBUG_ROUTING @@ -392,7 +456,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // Open the correct pipe for writing. // First, stop listening so we can talk - radio.stopListening(); + radio.openWritingPipe(out_pipe); radio.writeFast(frame_buffer, frame_size,multicast); ok = radio.txStandBy(txTimeout); @@ -448,7 +512,6 @@ bool RF24Network::is_direct_child( uint16_t node ) uint16_t child_node_mask = ( ~ node_mask ) << 3; result = ( node & child_node_mask ) == 0 ; } - return result; } @@ -527,7 +590,11 @@ bool is_valid_address( uint16_t node ) while(node) { uint8_t digit = node & 0B111; + #if !defined (RF24NetworkMulticast) if (digit < 1 || digit > 5) + #else + if (digit < 0 || digit > 5) //Allow our out of range multicast address + #endif { result = false; printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); @@ -539,27 +606,52 @@ bool is_valid_address( uint16_t node ) return result; } +/******************************************************************/ +#if defined (RF24NetworkMulticast) +void RF24Network::multicastLevel(uint8_t level){ + multicast_level = level; + radio.stopListening(); + radio.openReadingPipe(0,pipe_address(levelToAddress(level),0)); + radio.startListening(); + } + +uint16_t levelToAddress(uint8_t level){ + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + return levelAddr; +} +#endif /******************************************************************/ uint64_t pipe_address( uint16_t node, uint8_t pipe ) { - static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3 }; + static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3,0xec }; uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); // Translate the address to use our optimally chosen radio address bytes - uint8_t count = 0; uint16_t dec = node; + uint8_t count = 1; uint16_t dec = node; + #if defined (RF24NetworkMulticast) + if(pipe != 0 || !node){ + #endif while(dec){ - out[count]=address_translation[dec % 8]; // Convert our decimal values to octal, translate them to address bytes, and set our address + out[count]=address_translation[(dec % 8)]; // Convert our decimal values to octal, translate them to address bytes, and set our address dec /= 8; count++; - } - - //if( pipe > 0 ){ - result = result << 8; // Shift the result by 1 byte - out[0] = address_translation[pipe]; // Set last byte by pipe number - //} + } + + out[0] = address_translation[pipe]; // Set last byte by pipe number + #if defined (RF24NetworkMulticast) + }else{ + while(dec){ + dec/=8; + count++; + } + out[1] = address_translation[count-1]; + } + + #endif IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%lu: NET Pipe %i on node 0%o has address %lx%x\n\r"),millis(),pipe,node,*top,*out)); @@ -572,17 +664,11 @@ uint64_t pipe_address( uint16_t node, uint8_t pipe ) #if defined ENABLE_SLEEP_MODE -#if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) && !defined(__arm__) - -RF24NetworkHeader sleepHeader(/*to node*/ 00, /*type*/ 'S' /*Sleep*/); - -//bool awoke = 0; +#if !defined(__arm__) void wakeUp(){ - //detachInterrupt(0); sleep_disable(); sleep_cycles_remaining = 0; - //awoke = 1; } ISR(WDT_vect){ @@ -593,25 +679,27 @@ ISR(WDT_vect){ void RF24Network::sleepNode( unsigned int cycles, int interruptPin ){ - sleep_cycles_remaining = cycles; set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); if(interruptPin != 255){ attachInterrupt(interruptPin,wakeUp, LOW); - } + } + #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + WDTCR |= _BV(WDIE); + #else WDTCSR |= _BV(WDIE); + #endif while(sleep_cycles_remaining){ - //uint8_t junk = 23; - //write(&junk,1); sleep_mode(); // System sleeps here } // The WDT_vect interrupt wakes the MCU from here sleep_disable(); // System continues execution here when watchdog timed out - //if(awoke){ update(); awoke = 0; } detachInterrupt(interruptPin); - WDTCSR &= ~_BV(WDIE); - //radio.startListening(); - + #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + WDTCR &= ~_BV(WDIE); + #else + WDTCSR &= ~_BV(WDIE); + #endif } void RF24Network::setup_watchdog(uint8_t prescalar){ @@ -620,8 +708,14 @@ void RF24Network::setup_watchdog(uint8_t prescalar){ if ( prescalar & 8 ) wdtcsr |= _BV(WDP3); MCUSR &= ~_BV(WDRF); // Clear the WD System Reset Flag + + #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + WDTCR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset + WDTCR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU + #else WDTCSR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU + #endif } diff --git a/RF24Network.h b/RF24Network.h index d2a53a5f..3eee1afd 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -17,6 +17,8 @@ #include #include +#include "RF24Network_config.h" + class RF24; @@ -31,7 +33,7 @@ struct RF24NetworkHeader 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 routing messages */ + unsigned char reserved; /**< Reserved for future use */ static uint16_t next_id; /**< The message ID of the next message to be sent */ @@ -152,6 +154,21 @@ class RF24Network bool write(RF24NetworkHeader& header,const void* message, size_t len); bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + /** + * Send a multicast message to multiple nodes at once + * Allows messages to be rapidly broadcast through the network + * + * Multicasting is arranged in levels, with all nodes on the same level listening to the same address + * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 + * @see multicastLevel + * @param message Pointer to memory where the message is located + * @param len The size of the message + * @param level Multicast level to broadcast to + * @return Whether the message was successfully received + */ + + bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); + /** * 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 @@ -239,7 +256,22 @@ class RF24Network uint16_t routeTimeout; - + #if defined (RF24NetworkMulticast) + /** + * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, + * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option + * is used to override the defaults, and create custom multicast groups that all share a single + * address. + * The level should be specified in decimal format 1-6 + * @param level Levels 1 to 6 are available. All nodes at the same level will receive the same + * messages if in range. Messages will be routed in order of level, low to high by default. + */ + + void multicastLevel(uint8_t level); + + bool multicastRelay; + #endif + private: void open_pipes(void); uint16_t find_node( uint16_t current_node, uint16_t target_node ); @@ -259,6 +291,11 @@ class RF24Network #if defined (DUAL_HEAD_RADIO) RF24& radio1; +#endif +#if defined (RF24NetworkMulticast) + uint16_t lastMultiMessageID; + + uint8_t multicast_level; #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 */ diff --git a/RF24Network_config.h b/RF24Network_config.h index 741bb959..9c71675f 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -22,6 +22,7 @@ //#define DUAL_HEAD_RADIO //#define ENABLE_SLEEP_MODE +//#define RF24NetworkMulticast /*************************************/ @@ -98,6 +99,5 @@ #define PRIPSTR "%s" #endif - #endif // __RF24_CONFIG_H__ // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 04297604..964655a7 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -23,6 +23,9 @@ uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); +#if defined (RF24NetworkMulticast) +uint16_t levelToAddress( uint8_t level ); +#endif bool is_valid_address( uint16_t node ); uint32_t nFails = 0, nOK=0; @@ -54,9 +57,9 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; - radio.setRetries(retryVar, 15); - txTimeout = retryVar * 17; - routeTimeout = txTimeout+25; + radio.setRetries(retryVar, 5); + txTimeout = 20; + routeTimeout = txTimeout*10; // Setup our address helper cache setup_address(); @@ -66,7 +69,14 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) while (i--){ radio.openReadingPipe(i,pipe_address(_node_address,i)); } - //radio.setAutoAck(5,0); + #if defined (RF24NetworkMulticast) + uint8_t count = 0; uint16_t addy = _node_address; + while(addy){ + addy/=8; + count++; + } + multicast_level = count; + #endif radio.startListening(); } @@ -106,16 +116,41 @@ uint8_t RF24Network::update(void) // Is this for us? if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: Network ACK Rcvd\n")); + #endif return NETWORK_ACK; } - - // Add it to the buffer of frames for us - enqueue(); + enqueue(); + }else{ - // Relay it as a routed message - write(header.to_node,1); - } + + #if defined (RF24NetworkMulticast) + if( header.to_node == 0100){ + if(header.id != lastMultiMessageID){ + if(multicastRelay){ + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); + #endif + write(levelToAddress(multicast_level)<<3,4); + } + enqueue(); + lastMultiMessageID = header.id; + } + #ifdef SERIAL_DEBUG_ROUTING + else{ + printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); + } + #endif + }else{ + write(header.to_node,1); //Send it on, indicate it is a routed payload + } + #else + //if(radio.available()){printf("------FLUSHED DATA --------------");} + write(header.to_node,1); //Send it on, indicate it is a routed payload + #endif + } // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 @@ -219,6 +254,35 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) return bufsize; } +/******************************************************************/ +#if defined RF24NetworkMulticast + +bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level){ + // Fill out the header + + header.to_node = 0100; + header.from_node = node_address; + + // Build the full frame to send + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + if (len) + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); + if (len) + { + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); + } + + //uint16_t levelAddr = (level * 10)*8; + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + + return write(levelAddr,4); + +} +#endif + /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ return _write(header,message,len,070); @@ -280,33 +344,40 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Where do we send this? By default, to our parent uint16_t send_node = parent_node; // On which pipe - uint8_t send_pipe = parent_pipe; + uint8_t send_pipe = parent_pipe%5; + + if(directTo == 4){ + send_node = to_node; + send_pipe = 0; + + } - if(directTo>1){ + else if(directTo>1){ send_node = to_node; send_pipe = parent_pipe; + } // If the node is a direct child, else if ( is_direct_child(to_node) ) - { + { // Send directly send_node = to_node; // To its listening pipe - send_pipe = 0; + send_pipe = 5; } // If the node is a child of a child // talk on our child's listening pipe, // and let the direct child relay it. else if ( is_descendant(to_node) ) - { + { send_node = direct_child_route_to(to_node); - send_pipe = 0; + send_pipe = 5; } - if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3){ + if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ multicast = 1; } @@ -380,7 +451,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) radio.openWritingPipe(out_pipe); // Retry a few times - radio.writeFast(frame_buffer, frame_size, multicast); + radio.writeFast(frame_buffer, frame_size,multicast); ok = radio.txStandBy(); //ok = radio.write(frame_buffer,frame_size); @@ -496,7 +567,11 @@ bool is_valid_address( uint16_t node ) while(node) { uint8_t digit = node & 0B111; + #if !defined (RF24NetworkMulticast) if (digit < 1 || digit > 5) + #else + if (digit < 0 || digit > 5) //Allow our out of range multicast address + #endif { result = false; printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); @@ -508,46 +583,57 @@ bool is_valid_address( uint16_t node ) return result; } +/******************************************************************/ +#if defined (RF24NetworkMulticast) +void RF24Network::multicastLevel(uint8_t level){ + multicast_level = level; + radio.stopListening(); + radio.openReadingPipe(0,pipe_address(levelToAddress(level),0)); + radio.startListening(); + } + +uint16_t levelToAddress(uint8_t level){ + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + return levelAddr; +} +#endif + /******************************************************************/ uint64_t pipe_address( uint16_t node, uint8_t pipe ) { - - static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3 }; + static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3,0xec }; uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); // Translate the address to use our optimally chosen radio address bytes - uint8_t count = 0; uint16_t dec = node; + uint8_t count = 1; uint16_t dec = node; + #if defined (RF24NetworkMulticast) + if(pipe != 0 || !node){ + #endif while(dec){ - out[count]=address_translation[dec % 8]; // Convert our decimal values to octal, translate them to address bytes, and set our address + out[count]=address_translation[(dec % 8)]; // Convert our decimal values to octal, translate them to address bytes, and set our address dec /= 8; count++; - } - - //if( pipe > 0 ){ - result = result << 8; // Shift the result by 1 byte - out[0] = address_translation[pipe]; // Set last byte by pipe number - //} - /* - out[0] = pipe_segment[pipe]; - - uint8_t w; - short i = 4; - short shift = 12; - while(i--) - { - w = ( node >> shift ) & 0xF ; - w |= ~w << 4; - out[i+1] = w; - - shift -= 4; - } -*/ - IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%d: NET Pipe %i on node 0%o has address %x%x\n\r"),millis(),pipe,node,*top,*out)); - + } + + out[0] = address_translation[pipe]; // Set last byte by pipe number + #if defined (RF24NetworkMulticast) + }else{ + while(dec){ + dec/=8; + count++; + } + out[1] = address_translation[count-1]; + } + + #endif + + IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%u: NET Pipe %i on node 0%o has address %x%x\n\r"),millis(),pipe,node,*top,*out)); + return result; } -// vim:ai:cin:sts=2 sw=2 ft=cpp + diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index cfcd768f..acbe460f 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -22,6 +22,9 @@ #include #include #include +#include "RF24Network_config.h" + + class RF24; /** @@ -154,6 +157,24 @@ class RF24Network */ bool write(RF24NetworkHeader& header,const void* message, size_t len); bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + + #if defined RF24NetworkMulticast + /** + * Send a multicast message to multiple nodes at once + * Allows messages to be rapidly broadcast through the network + * + * Multicasting is arranged in levels, with all nodes on the same level listening to the same address + * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 + * @see multicastLevel + * @param message Pointer to memory where the message is located + * @param len The size of the message + * @param level Multicast level to broadcast to + * @return Whether the message was successfully received + */ + + bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); + #endif + /** * This node's parent address * @@ -183,7 +204,22 @@ class RF24Network uint16_t routeTimeout; - + #if defined (RF24NetworkMulticast) + /** + * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, + * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option + * is used to override the defaults, and create custom multicast groups that all share a single + * address. + * The level should be specified in decimal format 1-6 + * @param level Levels 1 to 6 are available. All nodes at the same level will receive the same + * messages if in range. Messages will be routed in order of level, low to high by default. + */ + + void multicastLevel(uint8_t level); + + bool multicastRelay; + #endif + protected: void open_pipes(void); uint16_t find_node( uint16_t current_node, uint16_t target_node ); @@ -199,11 +235,15 @@ class RF24Network bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); private: +#if defined (RF24NetworkMulticast) + uint16_t lastMultiMessageID; + uint8_t multicast_level; +#endif RF24& radio; /**< Underlying radio driver, provides link/physical layers */ 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 frame_queue[500*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. 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 */ diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 84e6e0fd..fff2b8f3 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -12,6 +12,12 @@ #include +/********** USER CONFIG **************/ + +//#define RF24NetworkMulticast + +/*************************************/ + // Stuff that is normally provided by Arduino #ifndef ARDUINO #include From b405da474f243a8782a4e463ea8675f2967c803b Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 6 Aug 2014 16:52:53 -0600 Subject: [PATCH 004/105] Small update to correct issues Corrected some minor issues --- RF24Network.cpp | 31 ++++++++++------ RPi/RF24Network/RF24Network.cpp | 9 ++--- RPi/RF24Network/RF24Network.h | 64 +++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 2be130f8..582806d9 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -55,7 +55,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) radio.enableDynamicAck(); // Use different retry periods to reduce data collisions - uint8_t retryVar = ((node_address % 6) *2) + 3; + uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); txTimeout = 20; routeTimeout = txTimeout+35; // About 2.5ms max delay per node at optimal routing speeds, 10 nodes maximum hop + max retry time for auto-ack + some delay to relieve congestion @@ -124,14 +124,14 @@ uint8_t RF24Network::update(void) if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd\n")); + printf_P(PSTR("MAC: Network ACK Rcvd \n")); #endif return NETWORK_ACK; } // Add it to the buffer of frames for us enqueue(); - nOK++; + }else{ @@ -317,10 +317,10 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself - if ( header.to_node == node_address ) + if ( header.to_node == node_address ){ // Just queue it in the received queue return enqueue(); - else + }else{ // Otherwise send it out over the air if(writeDirect != 070){ if(header.to_node == writeDirect){ @@ -331,7 +331,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l }else{ return write(header.to_node,0); } - + } } /******************************************************************/ @@ -386,19 +386,28 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); - + + #if !defined (DUAL_HEAD_RADIO) radio.stopListening(); + #endif + ok=write_to_pipe(send_node, send_pipe, multicast); #ifdef SERIAL_DEBUG_ROUTING if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } #endif - if( directTo == 1 && ok && !multicast ){ - frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address + + if( directTo == 1 && ok && !multicast ){ + frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address write(frame_buffer[0] | (frame_buffer[1] << 8),1); // Send it back as a routed message #if defined (SERIAL_DEBUG_ROUTING) + //if(!test){ printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),to_node,frame_buffer[0] | (frame_buffer[1] << 8)); + //}else{ + //printf("ACK send fail"); + //} + #endif } @@ -436,7 +445,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F } if(ok == true){ - //nOK++; + nOK++; }else{ nFails++; //printf("Fail to %o",to_node); } diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 964655a7..cde8de82 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -59,7 +59,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); txTimeout = 20; - routeTimeout = txTimeout*10; + routeTimeout = txTimeout+35; // Setup our address helper cache setup_address(); @@ -311,10 +311,10 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself - if ( header.to_node == node_address ) + if ( header.to_node == node_address ){ // Just queue it in the received queue return enqueue(); - else + }else{ if(writeDirect != 070){ if(header.to_node == writeDirect){ return write(writeDirect,2); @@ -325,6 +325,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // Otherwise send it out over the air return write(header.to_node,0); } + } } /******************************************************************/ @@ -452,7 +453,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // Retry a few times radio.writeFast(frame_buffer, frame_size,multicast); - ok = radio.txStandBy(); + ok = radio.txStandBy(txTimeout); //ok = radio.write(frame_buffer,frame_size); IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index acbe460f..d195259b 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -158,23 +158,6 @@ class RF24Network bool write(RF24NetworkHeader& header,const void* message, size_t len); bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); - #if defined RF24NetworkMulticast - /** - * Send a multicast message to multiple nodes at once - * Allows messages to be rapidly broadcast through the network - * - * Multicasting is arranged in levels, with all nodes on the same level listening to the same address - * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 - * @see multicastLevel - * @param message Pointer to memory where the message is located - * @param len The size of the message - * @param level Multicast level to broadcast to - * @return Whether the message was successfully received - */ - - bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); - #endif - /** * This node's parent address * @@ -203,22 +186,57 @@ class RF24Network */ uint16_t routeTimeout; + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the network in more advanced ways + */ + /**@{*/ + #if defined RF24NetworkMulticast + /** + * Send a multicast message to multiple nodes at once + * Allows messages to be rapidly broadcast through the network + * + * Multicasting is arranged in levels, with all nodes on the same level listening to the same address + * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 + * @see multicastLevel + * @param message Pointer to memory where the message is located + * @param len The size of the message + * @param level Multicast level to broadcast to + * @return Whether the message was successfully received + */ + + bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); - #if defined (RF24NetworkMulticast) /** * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option * is used to override the defaults, and create custom multicast groups that all share a single * address. - * The level should be specified in decimal format 1-6 + * The level should be specified in decimal format 1-6 + * Nodes can be configured to automatically forward multicast payloads to the next multicast level + * @see multicastRelay + * * @param level Levels 1 to 6 are available. All nodes at the same level will receive the same - * messages if in range. Messages will be routed in order of level, low to high by default. + * messages if in range. */ - void multicastLevel(uint8_t level); + void multicastLevel(uint8_t level); + /** + * Set individual nodes to relay received multicast payloads onto the next multicast level. + * Relay nodes will still receive the payloads, but they will also forward them on. + * Relay nodes can have a maximum of 4 direct child nodes, but can multicast to any number + * of nodes. + * Multicast nodes and relays are configured to filter out duplicate payloads, so having multiple + * relays in an area should not be a problem. + */ + bool multicastRelay; - #endif + +#endif protected: void open_pipes(void); @@ -243,7 +261,7 @@ class RF24Network 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[500*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. Space for a small set of frames that need to be delivered to the app layer */ + uint8_t frame_queue[255*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. 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 */ From 213ce12d954cd789257d1efbddcf15b8d47955be Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 15 Aug 2014 09:33:08 -0600 Subject: [PATCH 005/105] Changes to routing - Reverted to using auto-ack for all payloads except full multicast payloads and the first hop of manually routed payloads - Users can now address a payload logically to any node, and physically send it to any node. - Updated debug statements to provide better routing info --- RF24Network.cpp | 76 ++++++++++++++-------------- RF24Network.h | 7 +++ RF24Network_config.h | 8 +-- RPi/RF24Network/RF24Network.cpp | 56 ++++++++++---------- RPi/RF24Network/RF24Network_config.h | 5 +- 5 files changed, 80 insertions(+), 72 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 582806d9..784f98ee 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -57,8 +57,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); - txTimeout = 20; - routeTimeout = txTimeout+35; // About 2.5ms max delay per node at optimal routing speeds, 10 nodes maximum hop + max retry time for auto-ack + some delay to relieve congestion + txTimeout = 25; + routeTimeout = txTimeout*9; // Adjust for max delay per node //printf_P(PSTR("Retries: %d, txTimeout: %d"),retryVar,txTimeout); #if defined (DUAL_HEAD_RADIO) @@ -128,10 +128,9 @@ uint8_t RF24Network::update(void) #endif return NETWORK_ACK; } // Add it to the buffer of frames for us - + enqueue(); - - + }else{ @@ -188,7 +187,7 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(void) { bool result = false; - + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Enqueue @%x "),millis(),next_frame-frame_queue)); // Copy the current frame into the frame queue @@ -306,16 +305,18 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // Build the full frame to send memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); - if (len) + + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Sending %s\n\r"),millis(),header.toString())); + if (len){ memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,min(frame_size-sizeof(RF24NetworkHeader),len)); + //IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %08x\n\r"),millis(),*i)); + + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); + + //IF_SERIAL_DEBUG( printf_P(PSTR("%lu: NET message "),millis()); const uint16_t* charPtr[] = reinterpret_cast(message); printf("%x\n",*charPtr) ); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Sending %s\n\r"),millis(),header.toString())); - if (len) - { - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); } - // If the user is trying to send it to himself if ( header.to_node == node_address ){ // Just queue it in the received queue @@ -340,6 +341,8 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F { bool ok = false; bool multicast = 0; // Radio ACK requested = 0 + const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); + const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) @@ -352,21 +355,21 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F // On which pipe uint8_t send_pipe = parent_pipe %5; - if(directTo == 4){ - send_node = to_node; - send_pipe = 0; - } - else if(directTo>1){ - send_node = to_node; + if(directTo>1){ + send_node = to_node; + multicast = 1; + if(directTo == 4){ + send_pipe=0; + } } // If the node is a direct child, - else if ( is_direct_child(to_node) ) + else + if ( is_direct_child(to_node) ) { // Send directly send_node = to_node; - // To its listening pipe send_pipe = 5; } @@ -380,30 +383,29 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F } - if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ - multicast = 1; - } - + //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 4){ +// multicast = 0; + // } + // printf("Multi %d\n\r",multicast); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); - - #if !defined (DUAL_HEAD_RADIO) - radio.stopListening(); - #endif + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); - ok=write_to_pipe(send_node, send_pipe, multicast); + + + ok=write_to_pipe(send_node, send_pipe, multicast); + #ifdef SERIAL_DEBUG_ROUTING - if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } + if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress, send_node,send_pipe); } #endif - if( directTo == 1 && ok && !multicast ){ + if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address){ frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address - write(frame_buffer[0] | (frame_buffer[1] << 8),1); // Send it back as a routed message + write(fromAddress,1); // Send it back as a routed message #if defined (SERIAL_DEBUG_ROUTING) //if(!test){ - printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),to_node,frame_buffer[0] | (frame_buffer[1] << 8)); + printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),send_node,fromAddress); //}else{ //printf("ACK send fail"); //} @@ -431,12 +433,12 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F radio.startListening(); #endif - if( (send_node != to_node && directTo==0) || directTo == 3 ){ + if( (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%lu: MAC Network ACK fail from 0%o on pipe %x\n\r"),millis(),to_node,send_pipe); + printf_P(PSTR("%lu: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); #endif ok=0; break; @@ -465,7 +467,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // Open the correct pipe for writing. // First, stop listening so we can talk - + radio.stopListening(); radio.openWritingPipe(out_pipe); radio.writeFast(frame_buffer, frame_size,multicast); ok = radio.txStandBy(txTimeout); diff --git a/RF24Network.h b/RF24Network.h index 3eee1afd..5a06ac7c 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -269,7 +269,14 @@ class RF24Network void multicastLevel(uint8_t level); + /** + * Enabling this will allow this node to automatically forward received multicast frames to the next highest + * multicast level. Duplicate frames are filtered out, so multiple forwarding nodes at the same level should + * not interfere + */ + bool multicastRelay; + #endif private: diff --git a/RF24Network_config.h b/RF24Network_config.h index 9c71675f..d8399e8a 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -23,9 +23,10 @@ //#define DUAL_HEAD_RADIO //#define ENABLE_SLEEP_MODE //#define RF24NetworkMulticast - +//#define SERIAL_DEBUG +#define SERIAL_DEBUG_ROUTING /*************************************/ - + // Define _BV for non-Arduino platforms and for Arduino DUE #if defined (ARDUINO) && !defined (__arm__) @@ -52,8 +53,7 @@ #endif - #undef SERIAL_DEBUG - #define SERIAL_DEBUG_ROUTING + #if defined (SERIAL_DEBUG) #define IF_SERIAL_DEBUG(x) ({x;}) #else diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index cde8de82..c9c51940 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -103,8 +103,8 @@ uint8_t RF24Network::update(void) // Read the beginning of the frame as the header const RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); - IF_SERIAL_DEBUG(printf_P("%d: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%d: NET message %04x\n\r",millis(),*i)); + IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%u: NET message %04x\n\r",millis(),*i)); // Throw it away if it's not a valid address if ( !is_valid_address(header.to_node) ){ @@ -140,7 +140,7 @@ uint8_t RF24Network::update(void) } #ifdef SERIAL_DEBUG_ROUTING else{ - printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); + printf_P(PSTR("MAC: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); } #endif }else{ @@ -181,7 +181,7 @@ bool RF24Network::enqueue(void) { bool result = false; - IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Enqueue @%x "),millis(),next_frame-frame_queue)); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),next_frame-frame_queue)); // Copy the current frame into the frame queue if ( next_frame < frame_queue + sizeof(frame_queue) ) @@ -248,7 +248,7 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) memcpy(&header,frame,sizeof(RF24NetworkHeader)); memcpy(message,frame+sizeof(RF24NetworkHeader),bufsize); - IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Received %s\n\r"),millis(),header.toString())); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Received %s\n\r"),millis(),header.toString())); } return bufsize; @@ -303,10 +303,11 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l if (len) memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); - IF_SERIAL_DEBUG(printf_P(PSTR("%d: NET Sending %s\n\r"),millis(),header.toString())); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) { - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%d: NET message %04x\n\r"),millis(),*i)); + // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); + IF_SERIAL_DEBUG(printf("%u: NET message ",millis());const char* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf("\n\r") ); } @@ -334,6 +335,8 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { bool ok = false; bool multicast = 0; // Radio ACK requested = 0 + const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); + const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) @@ -347,16 +350,12 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // On which pipe uint8_t send_pipe = parent_pipe%5; - if(directTo == 4){ - send_node = to_node; - send_pipe = 0; - - } - - else if(directTo>1){ - send_node = to_node; - send_pipe = parent_pipe; - + if(directTo>1){ + send_node = to_node; + multicast = 1; + if(directTo == 4){ + send_pipe=0; + } } // If the node is a direct child, @@ -378,12 +377,11 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) } - if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ - multicast = 1; - } + //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ +// multicast = 1; + // } - IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe)); - + IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); // Put the frame on the pipe @@ -391,15 +389,15 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) //printf("Multi %d\n",multicast); #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } + if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); } #endif - if( directTo == 1 && ok && !multicast ){ + if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ){ frame_buffer[6] = NETWORK_ACK; frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(frame_buffer[0] | (frame_buffer[1] << 8),1); + write(fromAddress,1); #if defined (SERIAL_DEBUG_ROUTING) - printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,frame_buffer[0] | (frame_buffer[1] << 8)); + printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); #endif } @@ -418,13 +416,13 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Now, continue listening radio.startListening(); - if( (send_node != to_node && directTo==0) || directTo == 3 ){ + if( (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ ok=0; #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); + printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); #endif break; } @@ -456,7 +454,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) ok = radio.txStandBy(txTimeout); //ok = radio.write(frame_buffer,frame_size); - IF_SERIAL_DEBUG(printf_P(PSTR("%d: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); return ok; } diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index fff2b8f3..b32c2ac6 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -15,6 +15,8 @@ /********** USER CONFIG **************/ //#define RF24NetworkMulticast +//#define SERIAL_DEBUG //Change #undef to #define for debug +#define SERIAL_DEBUG_ROUTING /*************************************/ @@ -26,8 +28,7 @@ #define _BV(x) (1<<(x)) #endif -#undef SERIAL_DEBUG //Change #undef to #define for debug -#define SERIAL_DEBUG_ROUTING + #if defined (SERIAL_DEBUG) #define IF_SERIAL_DEBUG(x) ({x;}) #else From 5696f310f146b7da485d3de6a94987507d108f08 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 29 Aug 2014 15:29:35 +0200 Subject: [PATCH 006/105] Fix some indentation issues. Strip trailing spaces. Replace tabs with spaces. --- RPi/RF24Network/RF24Network.cpp | 337 +++++++++++++-------------- RPi/RF24Network/RF24Network.h | 106 ++++----- RPi/RF24Network/RF24Network_config.h | 2 +- 3 files changed, 222 insertions(+), 223 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index c9c51940..2a15ecd9 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -24,7 +24,7 @@ uint16_t RF24NetworkHeader::next_id = 1; uint64_t pipe_address( uint16_t node, uint8_t pipe ); #if defined (RF24NetworkMulticast) -uint16_t levelToAddress( uint8_t level ); + uint16_t levelToAddress( uint8_t level ); #endif bool is_valid_address( uint16_t node ); uint32_t nFails = 0, nOK=0; @@ -32,24 +32,22 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) -{ -} +{} /******************************************************************/ -void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) -{ - if (! is_valid_address(_node_address) ){ +void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { + if (! is_valid_address(_node_address) ) { return; -} + } node_address = _node_address; - if ( ! radio.isValid() ){ + if ( ! radio.isValid() ) { return; } - // Set up the radio the way we want it to look + // Set up the radio the way we want it to look radio.setChannel(_channel); radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); @@ -60,22 +58,22 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) radio.setRetries(retryVar, 5); txTimeout = 20; routeTimeout = txTimeout+35; - + // Setup our address helper cache setup_address(); // Open up all listening pipes int i = 6; - while (i--){ + while (i--) { radio.openReadingPipe(i,pipe_address(_node_address,i)); } - #if defined (RF24NetworkMulticast) + #if defined (RF24NetworkMulticast) uint8_t count = 0; uint16_t addy = _node_address; - while(addy){ - addy/=8; - count++; - } - multicast_level = count; + while(addy) { + addy/=8; + count++; + } + multicast_level = count; #endif radio.startListening(); @@ -83,8 +81,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) /******************************************************************/ void RF24Network::failures(uint32_t *_fails, uint32_t *_ok){ - *_fails = nFails; - *_ok = nOK; + *_fails = nFails; + *_ok = nOK; } uint8_t RF24Network::update(void) @@ -108,49 +106,49 @@ uint8_t RF24Network::update(void) // Throw it away if it's not a valid address if ( !is_valid_address(header.to_node) ){ - continue; - } - - - uint8_t res = header.type; + continue; + } + + + uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ - if(res == NETWORK_ACK){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd\n")); - #endif - return NETWORK_ACK; - } - enqueue(); - - + if(res == NETWORK_ACK){ + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: Network ACK Rcvd\n")); + #endif + return NETWORK_ACK; + } + enqueue(); + + }else{ - - #if defined (RF24NetworkMulticast) - if( header.to_node == 0100){ - if(header.id != lastMultiMessageID){ - if(multicastRelay){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); - #endif - write(levelToAddress(multicast_level)<<3,4); - } - enqueue(); - lastMultiMessageID = header.id; - } - #ifdef SERIAL_DEBUG_ROUTING - else{ - printf_P(PSTR("MAC: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); - } - #endif - }else{ - write(header.to_node,1); //Send it on, indicate it is a routed payload - } - #else - //if(radio.available()){printf("------FLUSHED DATA --------------");} - write(header.to_node,1); //Send it on, indicate it is a routed payload - #endif - } + + #if defined (RF24NetworkMulticast) + if( header.to_node == 0100){ + if(header.id != lastMultiMessageID){ + if(multicastRelay){ + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); + #endif + write(levelToAddress(multicast_level)<<3,4); + } + enqueue(); + lastMultiMessageID = header.id; + } + #ifdef SERIAL_DEBUG_ROUTING + else{ + printf_P(PSTR("MAC: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); + } + #endif + }else{ + write(header.to_node,1); //Send it on, indicate it is a routed payload + } + #else + //if(radio.available()){printf("------FLUSHED DATA --------------");} + write(header.to_node,1); //Send it on, indicate it is a routed payload + #endif + } // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 @@ -160,11 +158,11 @@ uint8_t RF24Network::update(void) if ( header.to_node == node_address && pipe_num == 0 && is_descendant(header.from_node) ) { - uint8_t pipe = pipe_to_descendant(header.from_node); - radio.openReadingPipe(pipe,pipe_address(node_address,pipe)); + uint8_t pipe = pipe_to_descendant(header.from_node); + radio.openReadingPipe(pipe,pipe_address(node_address,pipe)); - // Also need to open pipe 1 so the system can get the full 5-byte address of the pipe. - radio.openReadingPipe(1,pipe_address(node_address,1)); + // Also need to open pipe 1 so the system can get the full 5-byte address of the pipe. + radio.openReadingPipe(1,pipe_address(node_address,1)); } #endif //} @@ -258,11 +256,11 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) #if defined RF24NetworkMulticast bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level){ - // Fill out the header - - header.to_node = 0100; + // Fill out the header + + header.to_node = 0100; header.from_node = node_address; - + // Build the full frame to send memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); if (len) @@ -274,22 +272,22 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); } - //uint16_t levelAddr = (level * 10)*8; - uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); - - return write(levelAddr,4); - + //uint16_t levelAddr = (level * 10)*8; + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + + return write(levelAddr,4); + } #endif /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ - return _write(header,message,len,070); + return _write(header,message,len,070); } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ - return _write(header,message,len,writeDirect); + return _write(header,message,len,writeDirect); } /******************************************************************/ @@ -307,7 +305,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l if (len) { // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG(printf("%u: NET message ",millis());const char* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf("\n\r") ); + IF_SERIAL_DEBUG(printf("%u: NET message ",millis());const char* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf("\n\r") ); } @@ -316,16 +314,16 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // Just queue it in the received queue return enqueue(); }else{ - if(writeDirect != 070){ - if(header.to_node == writeDirect){ - return write(writeDirect,2); - }else{ - return write(writeDirect,3); - } + if(writeDirect != 070){ + if(header.to_node == writeDirect){ + return write(writeDirect,2); + }else{ + return write(writeDirect,3); + } }else{ - // Otherwise send it out over the air - return write(header.to_node,0); - } + // Otherwise send it out over the air + return write(header.to_node,0); + } } } @@ -337,7 +335,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) bool multicast = 0; // Radio ACK requested = 0 const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); - + // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) return false; @@ -349,18 +347,18 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) uint16_t send_node = parent_node; // On which pipe uint8_t send_pipe = parent_pipe%5; - - if(directTo>1){ - send_node = to_node; - multicast = 1; - if(directTo == 4){ - send_pipe=0; - } + + if(directTo>1){ + send_node = to_node; + multicast = 1; + if(directTo == 4){ + send_pipe=0; + } } - + // If the node is a direct child, else if ( is_direct_child(to_node) ) - { + { // Send directly send_node = to_node; @@ -371,38 +369,38 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // talk on our child's listening pipe, // and let the direct child relay it. else if ( is_descendant(to_node) ) - { + { send_node = direct_child_route_to(to_node); send_pipe = 5; } - + //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ -// multicast = 1; - // } - + // multicast = 1; + // } + IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); - + // Put the frame on the pipe - ok = write_to_pipe( send_node, send_pipe, multicast ); -//printf("Multi %d\n",multicast); - + ok = write_to_pipe( send_node, send_pipe, multicast ); + //printf("Multi %d\n",multicast); + #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); } + if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); } #endif - - if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ){ - frame_buffer[6] = NETWORK_ACK; - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(fromAddress,1); - #if defined (SERIAL_DEBUG_ROUTING) - printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); - #endif - } - - - + + if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ){ + frame_buffer[6] = NETWORK_ACK; + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; + write(fromAddress,1); + #if defined (SERIAL_DEBUG_ROUTING) + printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); + #endif + } + + + // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If we are talking on our talking pipe, it's possible that no one is listening. @@ -417,22 +415,22 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) radio.startListening(); if( (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ - uint32_t reply_time = millis(); - while( update() != NETWORK_ACK){ - if(millis() - reply_time > routeTimeout){ - ok=0; - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); - #endif - break; - } - } - //if(pOK){printf("pOK\n");} + uint32_t reply_time = millis(); + while( update() != NETWORK_ACK){ + if(millis() - reply_time > routeTimeout){ + ok=0; + #ifdef SERIAL_DEBUG_ROUTING + printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); + #endif + break; + } + } + //if(pOK){printf("pOK\n");} } if(ok == true){ - nOK++; - }else{ nFails++; - } + nOK++; + }else{ nFails++; + } return ok; } @@ -443,7 +441,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) bool ok = false; uint64_t out_pipe = pipe_address( node, pipe ); - + // First, stop listening so we can talk radio.stopListening(); // Open the correct pipe for writing. @@ -453,7 +451,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) radio.writeFast(frame_buffer, frame_size,multicast); ok = radio.txStandBy(txTimeout); //ok = radio.write(frame_buffer,frame_size); - + IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); return ok; @@ -522,7 +520,7 @@ void RF24Network::setup_address(void) { i >>= 3; m >>= 3; - } + } parent_pipe = i; //parent_pipe=i-1; @@ -566,11 +564,11 @@ bool is_valid_address( uint16_t node ) while(node) { uint8_t digit = node & 0B111; - #if !defined (RF24NetworkMulticast) + #if !defined (RF24NetworkMulticast) if (digit < 1 || digit > 5) - #else - if (digit < 0 || digit > 5) //Allow our out of range multicast address - #endif + #else + if (digit < 0 || digit > 5) //Allow our out of range multicast address + #endif { result = false; printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); @@ -586,53 +584,54 @@ bool is_valid_address( uint16_t node ) #if defined (RF24NetworkMulticast) void RF24Network::multicastLevel(uint8_t level){ multicast_level = level; - radio.stopListening(); + radio.stopListening(); radio.openReadingPipe(0,pipe_address(levelToAddress(level),0)); radio.startListening(); - } - +} + uint16_t levelToAddress(uint8_t level){ - uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); - return levelAddr; -} + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + return levelAddr; +} #endif /******************************************************************/ uint64_t pipe_address( uint16_t node, uint8_t pipe ) { - + static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3,0xec }; uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); - + // Translate the address to use our optimally chosen radio address bytes - uint8_t count = 1; uint16_t dec = node; - #if defined (RF24NetworkMulticast) - if(pipe != 0 || !node){ - #endif - while(dec){ - out[count]=address_translation[(dec % 8)]; // Convert our decimal values to octal, translate them to address bytes, and set our address - dec /= 8; - count++; - } - - out[0] = address_translation[pipe]; // Set last byte by pipe number - #if defined (RF24NetworkMulticast) - }else{ - while(dec){ - dec/=8; - count++; - } - out[1] = address_translation[count-1]; - } - + uint8_t count = 1; uint16_t dec = node; + #if defined (RF24NetworkMulticast) + if(pipe != 0 || !node){ #endif - + while(dec){ + out[count]=address_translation[(dec % 8)]; // Convert our decimal values to octal, translate them to address bytes, and set our address + dec /= 8; + count++; + } + + out[0] = address_translation[pipe]; // Set last byte by pipe number + #if defined (RF24NetworkMulticast) + }else{ + while(dec){ + dec/=8; + count++; + } + out[1] = address_translation[count-1]; + } + + #endif + IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%u: NET Pipe %i on node 0%o has address %x%x\n\r"),millis(),pipe,node,*top,*out)); - + return result; } +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index d195259b..f5a0d6f7 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -104,9 +104,9 @@ class RF24Network * @param _node_address The logical address of this node */ void begin(uint8_t _channel, uint16_t _node_address ); - + void failures(uint32_t *_fails, uint32_t *_ok); - + /** * Main layer loop * @@ -117,11 +117,11 @@ class RF24Network /** * 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 * @@ -143,7 +143,7 @@ class RF24Network * @return The total number of bytes copied into @p message */ size_t read(RF24NetworkHeader& header, void* message, size_t maxlen); - + /** * Send a message * @@ -151,20 +151,20 @@ class RF24Network * @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 + * @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); bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); - + /** * This node's parent address - * - * @return This node's parent address, or -1 if this is the base + * + * @return This node's parent address, or -1 if this is the base */ uint16_t parent() const; - + /** * @note: Optimization:This value is automatically assigned based on the node address * to reduce errors and increase throughput of the network. @@ -176,7 +176,7 @@ class RF24Network */ unsigned long txTimeout; - + /** * @note: Optimization: This new value defaults to 200 milliseconds. * This only affects payloads that are routed by one or more nodes. @@ -184,7 +184,7 @@ class RF24Network * Radios routing directly to their parent or children nodes do not * utilize this value. */ - + uint16_t routeTimeout; /**@}*/ @@ -194,12 +194,12 @@ class RF24Network * Methods you can use to drive the network in more advanced ways */ /**@{*/ - #if defined RF24NetworkMulticast +#if defined RF24NetworkMulticast /** * Send a multicast message to multiple nodes at once - * Allows messages to be rapidly broadcast through the network - * - * Multicasting is arranged in levels, with all nodes on the same level listening to the same address + * Allows messages to be rapidly broadcast through the network + * + * Multicasting is arranged in levels, with all nodes on the same level listening to the same address * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 * @see multicastLevel * @param message Pointer to memory where the message is located @@ -207,37 +207,37 @@ class RF24Network * @param level Multicast level to broadcast to * @return Whether the message was successfully received */ - - bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); - - /** - * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, - * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option - * is used to override the defaults, and create custom multicast groups that all share a single - * address. - * The level should be specified in decimal format 1-6 - * Nodes can be configured to automatically forward multicast payloads to the next multicast level - * @see multicastRelay - * - * @param level Levels 1 to 6 are available. All nodes at the same level will receive the same - * messages if in range. - */ - - void multicastLevel(uint8_t level); - - /** - * Set individual nodes to relay received multicast payloads onto the next multicast level. - * Relay nodes will still receive the payloads, but they will also forward them on. - * Relay nodes can have a maximum of 4 direct child nodes, but can multicast to any number - * of nodes. - * Multicast nodes and relays are configured to filter out duplicate payloads, so having multiple - * relays in an area should not be a problem. - */ - - bool multicastRelay; - -#endif - + + bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); + + /** + * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, + * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option + * is used to override the defaults, and create custom multicast groups that all share a single + * address. + * The level should be specified in decimal format 1-6 + * Nodes can be configured to automatically forward multicast payloads to the next multicast level + * @see multicastRelay + * + * @param level Levels 1 to 6 are available. All nodes at the same level will receive the same + * messages if in range. + */ + + void multicastLevel(uint8_t level); + + /** + * Set individual nodes to relay received multicast payloads onto the next multicast level. + * Relay nodes will still receive the payloads, but they will also forward them on. + * Relay nodes can have a maximum of 4 direct child nodes, but can multicast to any number + * of nodes. + * Multicast nodes and relays are configured to filter out duplicate payloads, so having multiple + * relays in an area should not be a problem. + */ + + bool multicastRelay; + +#endif + protected: void open_pipes(void); uint16_t find_node( uint16_t current_node, uint16_t target_node ); @@ -251,15 +251,15 @@ class RF24Network uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); - + private: #if defined (RF24NetworkMulticast) uint16_t lastMultiMessageID; uint8_t multicast_level; #endif - RF24& radio; /**< Underlying radio driver, provides link/physical layers */ + RF24& radio; /**< Underlying radio driver, provides link/physical layers */ 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 */ + 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[255*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. 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 */ @@ -277,7 +277,7 @@ class RF24Network * 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. - * + * */ /** diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index b32c2ac6..693e6715 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -54,7 +54,7 @@ typedef uint16_t prog_uint16_t; #define printf_P printf #define strlen_P strlen #define PROGMEM -#define pgm_read_word(p) (*(p)) +#define pgm_read_word(p) (*(p)) #define PRIPSTR "%s" #endif From fda8602c7beeb747b31175203f39bb9caf5c14dd Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 29 Aug 2014 15:50:29 +0200 Subject: [PATCH 007/105] Add a frame structure for each message. The frame consists of a header, the payload size and a message payload. This structure is only a helper struct for lib internals, to be able to use dynamic payload sizes. Over the air only the header and the payload (without the payload size) are transmitted. --- RPi/RF24Network/RF24Network.h | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index f5a0d6f7..c2a5e004 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -77,6 +77,52 @@ struct RF24NetworkHeader const char* toString(void) const; }; +/** + * Frame structure for each message + * + * The frame put over the air consists of a header and a message payload + */ +struct RF24NetworkFrame +{ + RF24NetworkHeader header; /**< Header which is sent with each message */ + uint8_t payload_size; /**< The size in bytes of the payload length */ + + /** + * Default constructor + * + * Simply constructs a blank frame + */ + RF24NetworkFrame() {} + + /** + * Send constructor + * + * Use this constructor to create a frame with header and then send a message + * + * @code + * RF24NetworkFrame frame(recipient_address,'t',sizeof(message)); + * network.write(frame,&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. + * @param _psize Length in bytes of the payload. + */ + RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, uint16_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) {} + + /** + * 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; +}; + /** * TMRh20 2014 - Optimized Network Layer for RF24 Radios * From 03a1bc7e47570c30a2316b7fa0841af1a9a64158 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 29 Aug 2014 16:27:52 +0200 Subject: [PATCH 008/105] Extendend the frame struct to allow setting payload. Define MAX_FRAME_SIZE as a makro with value of '32' (Bytes). --- RPi/RF24Network/RF24Network.h | 38 +++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index c2a5e004..d13287ce 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -24,6 +24,7 @@ #include #include "RF24Network_config.h" +#define MAX_FRAME_SIZE 32 class RF24; @@ -85,7 +86,8 @@ struct RF24NetworkHeader struct RF24NetworkFrame { RF24NetworkHeader header; /**< Header which is sent with each message */ - uint8_t payload_size; /**< The size in bytes of the payload length */ + size_t payload_size; /**< The size in bytes of the payload length */ + uint8_t payload_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame payload that will be sent/received over the air */ /** * Default constructor @@ -97,19 +99,43 @@ struct RF24NetworkFrame /** * Send constructor * - * Use this constructor to create a frame with header and then send a message + * Use this constructor to create a frame with header and payload and then send a message * * @code - * RF24NetworkFrame frame(recipient_address,'t',sizeof(message)); - * network.write(frame,&message,sizeof(message)); + * RF24NetworkFrame frame(recipient_address,'t',message,sizeof(message)); + * network.write(frame); * @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. + * @param _payload The message content. * @param _psize Length in bytes of the payload. */ - RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, uint16_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) {} + RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _payload = NULL, size_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) { + if (_payload != NULL) + memcpy(payload_buffer,_payload,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),_psize)); + } + + /** + * Send constructor + * + * Use this constructor to create a frame with header and payload and then send a message + * + * @code + * RF24NetworkHeader header(recipient_address,'t'); + * RF24NetworkFrame frame(header,message,sizeof(message)); + * network.write(frame); + * @endcode + * + * @param _header The header struct of the frame + * @param _payload The message content. + * @param _psize Length in bytes of the payload. + */ + RF24NetworkFrame(RF24NetworkHeader& _header, const void* _payload = NULL, size_t _psize = 0) : header(_header), payload_size(_psize) { + if (_payload != NULL) + memcpy(payload_buffer,_payload,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),_psize)); + } /** * Create debugging string @@ -305,7 +331,7 @@ class RF24Network #endif RF24& radio; /**< Underlying radio driver, provides link/physical layers */ 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 */ + const static int frame_size = MAX_FRAME_SIZE; /**< 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[255*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. 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 */ From 64147ff40ef55c11a7b48a605253cbf58808ecf0 Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 1 Sep 2014 10:29:57 +0200 Subject: [PATCH 009/105] Use the RF24NetworkFrame type for internal management of frames Changes in code: - internally use the introuduced RF24NetworkFrame type to describe and manage header and payload. - keep the write and read API for backward compatibility. - make needed changes in some functions to be able to use the RF24NetworkFrame and circular buffer. - extend the circular buffer class with some functions - front() - isEmpty() - isFull() - update copyrith TODO: - check if the code is supported in arduino - check for memory lecks - check for memory space usage (arduino) - implement automatic fragmentation and reassembly of large payloads - two independent tx and rx (circular buffer) FIFOs - clean up code - remove not need parts --- RPi/RF24Network/RF24Network.cpp | 68 +++++++++++----------- RPi/RF24Network/RF24Network.h | 8 ++- RPi/RF24Network/utils/CircularBuffer.h | 78 ++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 RPi/RF24Network/utils/CircularBuffer.h diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 2a15ecd9..3c65c591 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2014 Rei This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -31,7 +32,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) +RF24Network::RF24Network( RF24& _radio ): radio(_radio) {} /******************************************************************/ @@ -96,10 +97,17 @@ uint8_t RF24Network::update(void) //while (radio.available()) //{ // Fetch the payload, and see if this was the last one. - radio.read( frame_buffer, sizeof(frame_buffer) ); + size_t len = radio.getDynamicPayloadSize(); + radio.read( frame_buffer, len ); + + //Do we have a valid length for a frame? + //We need at least a frame with header a no payload (payload_size equals 0). + if (len < sizeof(RF24NetworkHeader)) + continue; // Read the beginning of the frame as the header - const RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); + RF24NetworkHeader header; + memcpy(&header,frame_buffer,sizeof(RF24NetworkHeader)); IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%u: NET message %04x\n\r",millis(),*i)); @@ -109,6 +117,9 @@ uint8_t RF24Network::update(void) continue; } + // Build the full frame + size_t payload_size = len-sizeof(RF24NetworkHeader); + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),payload_size); uint8_t res = header.type; // Is this for us? @@ -119,7 +130,7 @@ uint8_t RF24Network::update(void) #endif return NETWORK_ACK; } - enqueue(); + enqueue(frame); }else{ @@ -133,7 +144,7 @@ uint8_t RF24Network::update(void) #endif write(levelToAddress(multicast_level)<<3,4); } - enqueue(); + enqueue(frame); lastMultiMessageID = header.id; } #ifdef SERIAL_DEBUG_ROUTING @@ -170,28 +181,20 @@ uint8_t RF24Network::update(void) return 0; } - - - /******************************************************************/ -bool RF24Network::enqueue(void) -{ +bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),next_frame-frame_queue)); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.remain())); // Copy the current frame into the frame queue - if ( next_frame < frame_queue + sizeof(frame_queue) ) - { - memcpy(next_frame,frame_buffer, frame_size ); - next_frame += frame_size; + frame_queue.push(frame); + result = true; - result = true; + if (result) { IF_SERIAL_DEBUG(printf("ok\n\r")); - } - else - { + } else { IF_SERIAL_DEBUG(printf("failed\n\r")); } @@ -203,7 +206,7 @@ bool RF24Network::enqueue(void) bool RF24Network::available(void) { // Are there frames on the queue for us? - return (next_frame > frame_queue); + return (!frame_queue.isEmpty()); } /******************************************************************/ @@ -222,8 +225,8 @@ void RF24Network::peek(RF24NetworkHeader& header) { if ( available() ) { - // Copy the next available frame from the queue into the provided buffer - memcpy(&header,next_frame-frame_size,sizeof(RF24NetworkHeader)); + RF24NetworkFrame frame = frame_queue.front(); + memcpy(&header,&frame,sizeof(RF24NetworkHeader)); } } @@ -235,16 +238,13 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { - // Move the pointer back one in the queue - next_frame -= frame_size; - uint8_t* frame = next_frame; + RF24NetworkFrame frame = frame_queue.pop(); // How much buffer size should we actually copy? - bufsize = std::min(maxlen,frame_size-sizeof(RF24NetworkHeader)); + bufsize = std::min(frame.payload_size,maxlen); - // Copy the next available frame from the queue into the provided buffer - memcpy(&header,frame,sizeof(RF24NetworkHeader)); - memcpy(message,frame+sizeof(RF24NetworkHeader),bufsize); + memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); + memcpy(message,frame.payload_buffer,bufsize); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Received %s\n\r"),millis(),header.toString())); } @@ -262,9 +262,7 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ header.from_node = node_address; // Build the full frame to send - memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); - if (len) - memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) @@ -297,9 +295,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l header.from_node = node_address; // Build the full frame to send - memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); - if (len) - memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + RF24NetworkFrame frame = RF24NetworkFrame(header,message,sizeof(message)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) @@ -312,7 +308,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself if ( header.to_node == node_address ){ // Just queue it in the received queue - return enqueue(); + return enqueue(frame); }else{ if(writeDirect != 070){ if(header.to_node == writeDirect){ diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index d13287ce..34543e83 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -1,5 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2014 Rei This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,8 +24,10 @@ #include #include #include "RF24Network_config.h" +#include "utils/CircularBuffer.h" #define MAX_FRAME_SIZE 32 +#define MAX_FRAME_BUFFER_SIZE 255 class RF24; @@ -315,7 +318,7 @@ class RF24Network uint16_t find_node( uint16_t current_node, uint16_t target_node ); bool write(uint16_t, uint8_t directTo); bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); - bool enqueue(void); + bool enqueue(RF24NetworkFrame frame); bool is_direct_child( uint16_t node ); bool is_descendant( uint16_t node ); @@ -333,8 +336,7 @@ class RF24Network uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ const static int frame_size = MAX_FRAME_SIZE; /**< 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[255*frame_size]; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. 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 */ + CircularBuffer frame_queue; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. Space for a small set of frames that need to be delivered to the app layer */ uint16_t parent_node; /**< Our parent's node address */ uint8_t parent_pipe; /**< The pipe our parent uses to listen to us */ diff --git a/RPi/RF24Network/utils/CircularBuffer.h b/RPi/RF24Network/utils/CircularBuffer.h new file mode 100644 index 00000000..d950882c --- /dev/null +++ b/RPi/RF24Network/utils/CircularBuffer.h @@ -0,0 +1,78 @@ +/* + CircularBuffer.h - circular buffer library for Arduino. + + Copyright (c) 2009 Hiroki Yagita. + Copyright (c) 2014 Rei . + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef CIRCULARBUFFER_h +#define CIRCULARBUFFER_h +#include + +template +class CircularBuffer { +public: + enum { + Empty = 0, + Half = Size / 2, + Full = Size, + }; + + CircularBuffer() : + wp_(buf_), rp_(buf_), tail_(buf_+Size), remain_(0), size_(Size) {} + ~CircularBuffer() {} + void push(T value) { + *wp_++ = value; + remain_++; + if (wp_ == tail_) wp_ = buf_; + } + T pop() { + T result = *rp_++; + remain_--; + if (rp_ == tail_) rp_ = buf_; + return result; + } + T front() { + T result = *rp_; + return result; + } + int remain() const { + return remain_; + } + bool isEmpty() { + if (remain_) + return true; + return false; + } + bool isFull() { + return (size_ == remain_); + } + +private: + T buf_[Size]; + T *wp_; + T *rp_; + T *tail_; + uint16_t remain_; + uint16_t size_; +}; + +#endif From e1bfd7ae27b727c874ae764e5aadbf162849d82e Mon Sep 17 00:00:00 2001 From: Rei Date: Thu, 4 Sep 2014 10:27:03 +0200 Subject: [PATCH 010/105] Many changes in the fragmentation FIXME: problems with reassembly data structure --- RPi/RF24Network/{utils => }/CircularBuffer.h | 0 RPi/RF24Network/Makefile | 2 +- RPi/RF24Network/RF24Network.cpp | 100 +++++++++++++++++-- RPi/RF24Network/RF24Network.h | 65 ++++++++++-- RPi/RF24Network/lrucache.h | 74 ++++++++++++++ 5 files changed, 224 insertions(+), 17 deletions(-) rename RPi/RF24Network/{utils => }/CircularBuffer.h (100%) create mode 100644 RPi/RF24Network/lrucache.h diff --git a/RPi/RF24Network/utils/CircularBuffer.h b/RPi/RF24Network/CircularBuffer.h similarity index 100% rename from RPi/RF24Network/utils/CircularBuffer.h rename to RPi/RF24Network/CircularBuffer.h diff --git a/RPi/RF24Network/Makefile b/RPi/RF24Network/Makefile index 0fefb222..e0282f6b 100644 --- a/RPi/RF24Network/Makefile +++ b/RPi/RF24Network/Makefile @@ -24,7 +24,7 @@ LIBNAME_RFN=$(LIB_RFN).so.1.0 HEADER_DIR=${PREFIX}/include/RF24Network # The recommended compiler flags for the Raspberry Pi -CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -std=c++0x # make all # reinstall the library after each recompilation diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 3c65c591..9602eb73 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -186,11 +186,32 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.remain())); + if (frame.header.fragment_id > 0) { + IF_SERIAL_DEBUG(printf("%u: NET message as fragment %i received\n\r",millis(),header.fragment_id);); + + //if fragment_id > 1 then + if (frame.header.fragment_id > 1) { + //Enqueue the frame with it fragmented payload in the LRUCache + //The cache will assemble all payloads into one big payload + //!frameAssemblyCache.put(frame.header.from_node,frame); + result = true; + } else if (frame.header.fragment_id == 1) { + //if we got a fragment_id == 1, then we got the last fragment + //Enqueue this frame into the frame_queue + //!frame_queue.push( frameAssemblyCache.get(frame.header.from_node) ); + result = true; + } else { + // otherwise discard the message with the large payload + result = false; + } + + } else { + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.remain())); - // Copy the current frame into the frame queue - frame_queue.push(frame); - result = true; + // Copy the current frame into the frame queue + frame_queue.push(frame); + result = true; + } if (result) { IF_SERIAL_DEBUG(printf("ok\n\r")); @@ -241,10 +262,10 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) RF24NetworkFrame frame = frame_queue.pop(); // How much buffer size should we actually copy? - bufsize = std::min(frame.payload_size,maxlen); + bufsize = std::min(frame.getPayloadSize(),maxlen); memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); - memcpy(message,frame.payload_buffer,bufsize); + memcpy(message,frame.getPayloadArray(),bufsize); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Received %s\n\r"),millis(),header.toString())); } @@ -281,11 +302,71 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ - return _write(header,message,len,070); + return write(header,message,len,070); } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ + bool txSuccess = true; + + //Check payload size + if (len <= max_frame_payload_size) { + //If message payload length fits in a single message + //then use the normal _write() function return _write(header,message,len,writeDirect); + } + + if (len >= 255*max_frame_payload_size) { + //If the message payload is too big, whe cannot generate enough fragments + //and enumerate them + txSuccess = false; + return txSuccess; + } + //The payload is smaller than 6kBytes. We cann transmit it. + + //Divide the message payload into chuncks of max_frame_payload_size + uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) + uint8_t msgCount = 0; + + IF_SERIAL_DEBUG(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); + + //Iterate over the payload chuncks + // Assemble a new message, copy and fill out the header + // Try to send this message + // If it fails + // then break + // return result as false + while (fragment_id > 0) { + + //Copy and fill out the header + RF24NetworkHeader fragmentHeader; + fragmentHeader = header; + fragmentHeader.fragment_id = fragment_id; + + size_t offset = msgCount*max_frame_payload_size; + + IF_SERIAL_DEBUG(printf("%u: NET try to transmit msg with fragment '%i'\n\r",millis(),(msgCount+1));); + + //Try to send the payload chunk with the copied header + bool ok = _write(fragmentHeader,message+offset,len-offset,writeDirect); + if (!ok) { + IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),(msgCount+1));); + txSuccess = false; + break; + } + IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),(msgCount+1));); + + //Message was successful sent + //Check and modify counters + fragment_id--; + msgCount++; + + IF_SERIAL_DEBUG(printf("%u: NET DEBUG: fragmentID=%i msgCount=%i loop_done?=%i\n\r",millis(),fragment_id,msgCount,!(fragment_id > 0));); + } + + //Return true if all the chuncks where sent successfuly + //else return false + IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. Status",millis(),msgCount); printf("%s\n\r", ok ? "ok" : "not ok");); + return txSuccess; } /******************************************************************/ @@ -295,7 +376,8 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l header.from_node = node_address; // Build the full frame to send - RF24NetworkFrame frame = RF24NetworkFrame(header,message,sizeof(message)); + if (len) + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) @@ -307,6 +389,8 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself if ( header.to_node == node_address ){ + // Build the frame to send + RF24NetworkFrame frame = RF24NetworkFrame(header,message,sizeof(message)); // Just queue it in the received queue return enqueue(frame); }else{ diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 34543e83..e28454c1 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -17,17 +17,24 @@ * Class declaration for RF24Network */ + #include #include #include #include #include #include +#include +//!#include +#include #include "RF24Network_config.h" -#include "utils/CircularBuffer.h" +#include "CircularBuffer.h" +#include "FrameLRUCache.h" #define MAX_FRAME_SIZE 32 #define MAX_FRAME_BUFFER_SIZE 255 +#define MAX_PAYLOAD_SIZE ((MAX_FRAME_SIZE-sizeof(RF24NetworkHeader))*255) +#define MAX_LRU_CACHE_SIZE 32 class RF24; @@ -42,7 +49,7 @@ struct RF24NetworkHeader 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 */ + unsigned char fragment_id; /**< Used to count the number of fragments of the payload. Zero (0) means no more fragments left. */ static uint16_t next_id; /**< The message ID of the next message to be sent */ @@ -90,7 +97,7 @@ struct RF24NetworkFrame { RF24NetworkHeader header; /**< Header which is sent with each message */ size_t payload_size; /**< The size in bytes of the payload length */ - uint8_t payload_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame payload that will be sent/received over the air */ + std::list payload_buffer; /**< Vector to put the frame payload that will be sent/received over the air */ /** * Default constructor @@ -116,8 +123,7 @@ struct RF24NetworkFrame * @param _psize Length in bytes of the payload. */ RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _payload = NULL, size_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) { - if (_payload != NULL) - memcpy(payload_buffer,_payload,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),_psize)); + appendToPayload(_payload,payload_size); } /** @@ -136,8 +142,7 @@ struct RF24NetworkFrame * @param _psize Length in bytes of the payload. */ RF24NetworkFrame(RF24NetworkHeader& _header, const void* _payload = NULL, size_t _psize = 0) : header(_header), payload_size(_psize) { - if (_payload != NULL) - memcpy(payload_buffer,_payload,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),_psize)); + appendToPayload(_payload,payload_size); } /** @@ -150,6 +155,48 @@ struct RF24NetworkFrame * @return String representation of this object */ const char* toString(void) const; + + /** + * Get the frame payload (message) as array + * + * Internally is the payload a vector. + * @return array pointer to the payload + */ + const void* getPayloadArray(void) { + //!const void* res = &payload_buffer[0]; + //!return res; + uint8_t *arr = new uint8_t[payload_buffer.size()]; + std::copy(payload_buffer.begin(),payload_buffer.end(),arr); + return arr; + } + + /** + * Get the payload size + * + * @return Size in bytes of the current payload + */ + size_t getPayloadSize(void) { + return payload_buffer.size(); + } + + /** + * Append the given payload to the current payload_buffer + * + */ + void appendToPayload(const void* payload, size_t len) { + if (payload && len) { + if ((payload_buffer.size()+len) <= MAX_PAYLOAD_SIZE) { + //Cast the payload as uint8_t + const uint8_t *q = (const uint8_t *)payload; + //Append the payload to the buffer + payload_buffer.insert(payload_buffer.end(), q, q+len); + //Save space in RAM + //!payload_buffer.shrink_to_fit(); + } + } + //Set the new payload_buffer size + payload_size = payload_buffer.size(); + }; }; /** @@ -334,9 +381,11 @@ class RF24Network #endif RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ - const static int frame_size = MAX_FRAME_SIZE; /**< How large is each frame over the air */ + const static unsigned int frame_size = MAX_FRAME_SIZE; /**< How large is each frame over the air */ + const static unsigned int max_frame_payload_size = frame_size-sizeof(RF24NetworkHeader); uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ CircularBuffer frame_queue; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. Space for a small set of frames that need to be delivered to the app layer */ + //!FrameLRUCache frameAssemblyCache; uint16_t parent_node; /**< Our parent's node address */ uint8_t parent_pipe; /**< The pipe our parent uses to listen to us */ diff --git a/RPi/RF24Network/lrucache.h b/RPi/RF24Network/lrucache.h new file mode 100644 index 00000000..0eabfa2a --- /dev/null +++ b/RPi/RF24Network/lrucache.h @@ -0,0 +1,74 @@ +/* + * File: lrucache.hpp + * Author: Alexander Ponomarev + * https://github.com/aponomarev/cpp-lru-cache/blob/master/include/lrucache.hpp + * + * Created on June 20, 2013, 5:09 PM + */ + +#ifndef _LRUCACHE_HPP_INCLUDED_ +#define _LRUCACHE_HPP_INCLUDED_ + +#include +#include +#include +#include + +namespace cache { + +template +class lru_cache { +public: + typedef typename std::pair key_value_pair_t; + typedef typename std::list::iterator list_iterator_t; + + lru_cache(size_t max_size) : + _max_size(max_size) { + } + + void put(const key_t& key, const value_t& value) { + auto it = _cache_items_map.find(key); + if (it != _cache_items_map.end()) { + _cache_items_list.erase(it->second); + _cache_items_map.erase(it); + } + + _cache_items_list.push_front(key_value_pair_t(key, value)); + _cache_items_map[key] = _cache_items_list.begin(); + + if (_cache_items_map.size() > _max_size) { + auto last = _cache_items_list.end(); + last--; + _cache_items_map.erase(last->first); + _cache_items_list.pop_back(); + } + } + + const value_t& get(const key_t& key) { + auto it = _cache_items_map.find(key); + if (it == _cache_items_map.end()) { + throw std::range_error("There is no such key in cache"); + } else { + _cache_items_list.splice(_cache_items_list.begin(), _cache_items_list, it->second); + return it->second->second; + } + } + + bool exists(const key_t& key) const { + return _cache_items_map.find(key) != _cache_items_map.end(); + } + + size_t size() const { + return _cache_items_map.size(); + } + +private: + std::list _cache_items_list; + std::unordered_map _cache_items_map; + size_t _max_size; +}; + +} // namespace lru + +#endif /* _LRUCACHE_HPP_INCLUDED_ */ + From a20004f4f5d5576dab59fe3e7a6b45ebb23be0e9 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 5 Sep 2014 16:55:11 +0200 Subject: [PATCH 011/105] Change isEmpty test --- RPi/RF24Network/CircularBuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RPi/RF24Network/CircularBuffer.h b/RPi/RF24Network/CircularBuffer.h index d950882c..22d3c236 100644 --- a/RPi/RF24Network/CircularBuffer.h +++ b/RPi/RF24Network/CircularBuffer.h @@ -58,7 +58,7 @@ class CircularBuffer { return remain_; } bool isEmpty() { - if (remain_) + if (remain_ > 0) return true; return false; } From 49670849f32182b719f36efceea3d11a6066939f Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 5 Sep 2014 17:14:39 +0200 Subject: [PATCH 012/105] Delete not needed data structure --- RPi/RF24Network/lrucache.h | 74 -------------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 RPi/RF24Network/lrucache.h diff --git a/RPi/RF24Network/lrucache.h b/RPi/RF24Network/lrucache.h deleted file mode 100644 index 0eabfa2a..00000000 --- a/RPi/RF24Network/lrucache.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * File: lrucache.hpp - * Author: Alexander Ponomarev - * https://github.com/aponomarev/cpp-lru-cache/blob/master/include/lrucache.hpp - * - * Created on June 20, 2013, 5:09 PM - */ - -#ifndef _LRUCACHE_HPP_INCLUDED_ -#define _LRUCACHE_HPP_INCLUDED_ - -#include -#include -#include -#include - -namespace cache { - -template -class lru_cache { -public: - typedef typename std::pair key_value_pair_t; - typedef typename std::list::iterator list_iterator_t; - - lru_cache(size_t max_size) : - _max_size(max_size) { - } - - void put(const key_t& key, const value_t& value) { - auto it = _cache_items_map.find(key); - if (it != _cache_items_map.end()) { - _cache_items_list.erase(it->second); - _cache_items_map.erase(it); - } - - _cache_items_list.push_front(key_value_pair_t(key, value)); - _cache_items_map[key] = _cache_items_list.begin(); - - if (_cache_items_map.size() > _max_size) { - auto last = _cache_items_list.end(); - last--; - _cache_items_map.erase(last->first); - _cache_items_list.pop_back(); - } - } - - const value_t& get(const key_t& key) { - auto it = _cache_items_map.find(key); - if (it == _cache_items_map.end()) { - throw std::range_error("There is no such key in cache"); - } else { - _cache_items_list.splice(_cache_items_list.begin(), _cache_items_list, it->second); - return it->second->second; - } - } - - bool exists(const key_t& key) const { - return _cache_items_map.find(key) != _cache_items_map.end(); - } - - size_t size() const { - return _cache_items_map.size(); - } - -private: - std::list _cache_items_list; - std::unordered_map _cache_items_map; - size_t _max_size; -}; - -} // namespace lru - -#endif /* _LRUCACHE_HPP_INCLUDED_ */ - From a4dbdd149a4b2c81f97f95ed2de4249aa08f78c5 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 5 Sep 2014 17:14:51 +0200 Subject: [PATCH 013/105] Fully functional fragmentation implementation for large payloads. Fragmentation is transparent to caller application. TODO: - multicast fragmentation - test with multiple nodes in a larger network - use a circular queue for the frame_queue data structure - use a LRUCache data structure for the frame reassembly - lower memory consumption - arduino porting - cleanup code - remove redundant and innecessary code - Based on IPv4 fragmentation https://tools.ietf.org/html/rfc791. - Tested only on Raspberry Pi, may not work in Arduinos due to memory usage Tested only with two raspberris using NRF24L01+ 10 meters appart. - The lib is capable to detect if the message payload is bigger than frame_size-sizeof(Header) and split the payload into chuncks and send them separately. - This commit introduces two new flag types: - NETWORK_MORE_FRAGMENTS - NETWORK_LAST_FRAGMENT - As of IPv4 fragmention this flags are used to mark the frames to notify the receiver if more fragments will come or this is the last fragment. - The previous _write() function to transmit the frames is used. - Changes in payload size management were made to allow dynamic payload sizes without the need to define a field in the header. - Introduce a new struct type "RF24NetworkFrame" for better frame handling inside the lib. This type encapsulates the header, the current size of the payload and a (fixed size) array containing the payload. - The frame_buffer data structure was changes in favor of a std::queue for better handling. - Make some changes renaming variables and defining the values. - Use a std::map with double key (header.to_from,header.id) to identify the incoming fragments, allowing the reassembly of multiple transmission simultaneously. - The reassembly of frames hapens in the enqueue(frame) function. Based on the fragment_id and header.id the fragments are handled diffently and in the end the completely assembled frames are queued in the frame_queue presenting them to the calling application. - Introduce a new debug flag to print fragmentation debug messages. - Use definitions for static values like frame size or payload max size. - Strip trailing spaces and fix some indentation issues. --- RPi/RF24Network/RF24Network.cpp | 149 ++++++++++++++++++--------- RPi/RF24Network/RF24Network.h | 111 ++++++-------------- RPi/RF24Network/RF24Network_config.h | 9 +- 3 files changed, 138 insertions(+), 131 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 9602eb73..0f248de4 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -32,7 +32,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE) {} /******************************************************************/ @@ -53,6 +53,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); radio.enableDynamicAck(); + radio.enableDynamicPayloads(); //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; @@ -110,16 +111,20 @@ uint8_t RF24Network::update(void) memcpy(&header,frame_buffer,sizeof(RF24NetworkHeader)); IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%u: NET message %04x\n\r",millis(),*i)); + if (len) { + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),len);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + } // Throw it away if it's not a valid address if ( !is_valid_address(header.to_node) ){ continue; } + IF_SERIAL_DEBUG(printf_P("%u: MAC Valid frame from %i with size %i received.\n\r",millis(),header.from_node,len)); + // Build the full frame - size_t payload_size = len-sizeof(RF24NetworkHeader); - RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),payload_size); + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),len-sizeof(RF24NetworkHeader)); uint8_t res = header.type; // Is this for us? @@ -186,27 +191,30 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - if (frame.header.fragment_id > 0) { - IF_SERIAL_DEBUG(printf("%u: NET message as fragment %i received\n\r",millis(),header.fragment_id);); - - //if fragment_id > 1 then - if (frame.header.fragment_id > 1) { - //Enqueue the frame with it fragmented payload in the LRUCache - //The cache will assemble all payloads into one big payload - //!frameAssemblyCache.put(frame.header.from_node,frame); - result = true; - } else if (frame.header.fragment_id == 1) { - //if we got a fragment_id == 1, then we got the last fragment - //Enqueue this frame into the frame_queue - //!frame_queue.push( frameAssemblyCache.get(frame.header.from_node) ); - result = true; - } else { - // otherwise discard the message with the large payload - result = false; - } + if (frame.header.fragment_id > 1 && frame.header.type == NETWORK_MORE_FRAGMENTS) { + //Set the more fragments flag to indicate a fragmented frame + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + + //Append payload + appendFragmentToFrame(frame); + result = true; + + } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { + //Set the last fragment flag to indicate the last fragment + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + + //Append payload + appendFragmentToFrame(frame); + + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); + //Push the assembled frame in the frame_queue and remove it from cache + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + + result = true; } else { - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.remain())); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); // Copy the current frame into the frame queue frame_queue.push(frame); @@ -224,10 +232,31 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { /******************************************************************/ +void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { + + if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { + //This is the first of many fragments + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + } else { + //We have at least received one fragments. + + //Append payload + RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); + + //Increment message size + f->message_size += frame.message_size; + //Update header + f->header = frame.header; + } +} + +/******************************************************************/ + bool RF24Network::available(void) { // Are there frames on the queue for us? - return (!frame_queue.isEmpty()); + return (!frame_queue.empty()); } /******************************************************************/ @@ -259,17 +288,20 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { - RF24NetworkFrame frame = frame_queue.pop(); + RF24NetworkFrame frame = frame_queue.front(); // How much buffer size should we actually copy? - bufsize = std::min(frame.getPayloadSize(),maxlen); + bufsize = std::min(frame.message_size,maxlen); memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); - memcpy(message,frame.getPayloadArray(),bufsize); + memcpy(message,frame.message_buffer,bufsize); - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Received %s\n\r"),millis(),header.toString())); - } + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message size %i\n",millis(),frame.message_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET readed %s\n\r"),millis(),header.toString())); + frame_queue.pop(); + } return bufsize; } @@ -309,25 +341,33 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le bool txSuccess = true; //Check payload size + if (len > MAX_PAYLOAD_SIZE) { + IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size of %i\n\r",millis(),MAX_PAYLOAD_SIZE);); + return false; + } + + //If message payload length fits in a single message + //then use the normal _write() function if (len <= max_frame_payload_size) { - //If message payload length fits in a single message - //then use the normal _write() function return _write(header,message,len,writeDirect); } - if (len >= 255*max_frame_payload_size) { - //If the message payload is too big, whe cannot generate enough fragments - //and enumerate them + //If the message payload is too big, whe cannot generate enough fragments + //and enumerate them + if (len > 255*max_frame_payload_size) { + txSuccess = false; return txSuccess; } - //The payload is smaller than 6kBytes. We cann transmit it. + + //The payload is smaller than MAX_PAYLOAD_SIZE and we can enumerate the fragments. + // --> We cann transmit the message. //Divide the message payload into chuncks of max_frame_payload_size uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) uint8_t msgCount = 0; - IF_SERIAL_DEBUG(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); //Iterate over the payload chuncks // Assemble a new message, copy and fill out the header @@ -338,34 +378,38 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le while (fragment_id > 0) { //Copy and fill out the header - RF24NetworkHeader fragmentHeader; - fragmentHeader = header; + RF24NetworkHeader fragmentHeader = header; fragmentHeader.fragment_id = fragment_id; + if (fragment_id == 1) { + fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + } else { + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } + size_t offset = msgCount*max_frame_payload_size; + size_t fragmentLen = std::min(len-offset,max_frame_payload_size); - IF_SERIAL_DEBUG(printf("%u: NET try to transmit msg with fragment '%i'\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header - bool ok = _write(fragmentHeader,message+offset,len-offset,writeDirect); + bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); if (!ok) { - IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); txSuccess = false; break; } - IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); //Message was successful sent //Check and modify counters fragment_id--; msgCount++; - - IF_SERIAL_DEBUG(printf("%u: NET DEBUG: fragmentID=%i msgCount=%i loop_done?=%i\n\r",millis(),fragment_id,msgCount,!(fragment_id > 0));); } //Return true if all the chuncks where sent successfuly //else return false - IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. Status",millis(),msgCount); printf("%s\n\r", ok ? "ok" : "not ok");); + IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return txSuccess; } /******************************************************************/ @@ -376,21 +420,26 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l header.from_node = node_address; // Build the full frame to send - if (len) - memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + frame_size = sizeof(RF24NetworkHeader); //Set the current frame size + if (len) { + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); + frame_size += len; //Set the current frame size + } IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); - if (len) + if (frame_size) { // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG(printf("%u: NET message ",millis());const char* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf("\n\r") ); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),frame_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } // If the user is trying to send it to himself if ( header.to_node == node_address ){ // Build the frame to send - RF24NetworkFrame frame = RF24NetworkFrame(header,message,sizeof(message)); + RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); // Just queue it in the received queue return enqueue(frame); }else{ diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index e28454c1..c0d16fd4 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -25,17 +25,23 @@ #include #include #include -//!#include -#include +#include +#include // std::pair +#include #include "RF24Network_config.h" -#include "CircularBuffer.h" -#include "FrameLRUCache.h" #define MAX_FRAME_SIZE 32 #define MAX_FRAME_BUFFER_SIZE 255 -#define MAX_PAYLOAD_SIZE ((MAX_FRAME_SIZE-sizeof(RF24NetworkHeader))*255) +#define MAX_PAYLOAD_SIZE 1500 #define MAX_LRU_CACHE_SIZE 32 +//Network header message types +#define NETWORK_ACK_REQUEST 128 +#define NETWORK_ACK 129 +#define NETWORK_MORE_FRAGMENTS 130 +#define NETWORK_LAST_FRAGMENT 131 + + class RF24; /** @@ -96,8 +102,8 @@ struct RF24NetworkHeader struct RF24NetworkFrame { RF24NetworkHeader header; /**< Header which is sent with each message */ - size_t payload_size; /**< The size in bytes of the payload length */ - std::list payload_buffer; /**< Vector to put the frame payload that will be sent/received over the air */ + size_t message_size; /**< The size in bytes of the payload length */ + uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air */ /** * Default constructor @@ -110,39 +116,24 @@ struct RF24NetworkFrame * Send constructor * * Use this constructor to create a frame with header and payload and then send a message - * - * @code - * RF24NetworkFrame frame(recipient_address,'t',message,sizeof(message)); - * network.write(frame); - * @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. - * @param _payload The message content. - * @param _psize Length in bytes of the payload. */ - RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _payload = NULL, size_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) { - appendToPayload(_payload,payload_size); + RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _message = NULL, size_t _len = 0) : + header(RF24NetworkHeader(_to,_type)), message_size(_len) { + if (_message && _len) { + memcpy(message_buffer,_message,_len); + } } /** * Send constructor * * Use this constructor to create a frame with header and payload and then send a message - * - * @code - * RF24NetworkHeader header(recipient_address,'t'); - * RF24NetworkFrame frame(header,message,sizeof(message)); - * network.write(frame); - * @endcode - * - * @param _header The header struct of the frame - * @param _payload The message content. - * @param _psize Length in bytes of the payload. */ - RF24NetworkFrame(RF24NetworkHeader& _header, const void* _payload = NULL, size_t _psize = 0) : header(_header), payload_size(_psize) { - appendToPayload(_payload,payload_size); + RF24NetworkFrame(RF24NetworkHeader& _header, const void* _message = NULL, size_t _len = 0) : + header(_header), message_size(_len) { + if (_message && _len) { + memcpy(message_buffer,_message,_len); + } } /** @@ -156,47 +147,6 @@ struct RF24NetworkFrame */ const char* toString(void) const; - /** - * Get the frame payload (message) as array - * - * Internally is the payload a vector. - * @return array pointer to the payload - */ - const void* getPayloadArray(void) { - //!const void* res = &payload_buffer[0]; - //!return res; - uint8_t *arr = new uint8_t[payload_buffer.size()]; - std::copy(payload_buffer.begin(),payload_buffer.end(),arr); - return arr; - } - - /** - * Get the payload size - * - * @return Size in bytes of the current payload - */ - size_t getPayloadSize(void) { - return payload_buffer.size(); - } - - /** - * Append the given payload to the current payload_buffer - * - */ - void appendToPayload(const void* payload, size_t len) { - if (payload && len) { - if ((payload_buffer.size()+len) <= MAX_PAYLOAD_SIZE) { - //Cast the payload as uint8_t - const uint8_t *q = (const uint8_t *)payload; - //Append the payload to the buffer - payload_buffer.insert(payload_buffer.end(), q, q+len); - //Save space in RAM - //!payload_buffer.shrink_to_fit(); - } - } - //Set the new payload_buffer size - payload_size = payload_buffer.size(); - }; }; /** @@ -373,6 +323,7 @@ class RF24Network uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + void appendFragmentToFrame(RF24NetworkFrame frame); private: #if defined (RF24NetworkMulticast) @@ -381,17 +332,17 @@ class RF24Network #endif RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ - const static unsigned int frame_size = MAX_FRAME_SIZE; /**< How large is each frame over the air */ - const static unsigned int max_frame_payload_size = frame_size-sizeof(RF24NetworkHeader); - uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ - CircularBuffer frame_queue; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. Space for a small set of frames that need to be delivered to the app layer */ - //!FrameLRUCache frameAssemblyCache; + uint8_t frame_size; /**< How large is each frame over the air */ + const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); + uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ + std::queue frame_queue; + std::map, RF24NetworkFrame> frameFragmentsCache; + 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 */ - #define NETWORK_ACK_REQUEST 128 - #define NETWORK_ACK 129 + }; /** diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 693e6715..fd3de785 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -15,8 +15,9 @@ /********** USER CONFIG **************/ //#define RF24NetworkMulticast -//#define SERIAL_DEBUG //Change #undef to #define for debug +#define SERIAL_DEBUG //Change #undef to #define for debug #define SERIAL_DEBUG_ROUTING +//#define SERIAL_DEBUG_FRAGMENTATION /*************************************/ @@ -35,6 +36,12 @@ #define IF_SERIAL_DEBUG(x) #endif +#if defined (SERIAL_DEBUG_FRAGMENTATION) +#define IF_SERIAL_DEBUG_FRAGMENTATION(x) ({x;}) +#else +#define IF_SERIAL_DEBUG_FRAGMENTATION(x) +#endif + // Avoid spurious warnings #if ! defined( NATIVE ) && defined( ARDUINO ) #undef PROGMEM From d49e9579b1bdf173606084223c0bf42175a7cb66 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 5 Sep 2014 17:14:51 +0200 Subject: [PATCH 014/105] Fully functional fragmentation and reassembly implementation for large payloads. Fragmentation is transparent to calling application. TODO: - multicast fragmentation - test with multiple nodes in a larger network - use a circular queue for the frame_queue data structure - use a LRUCache data structure for the frame reassembly - lower memory consumption - arduino porting - cleanup code - remove redundant and unnecessary code - Based on IPv4 fragmentation https://tools.ietf.org/html/rfc791. - Tested only on Raspberry Pi, may not work in Arduinos due to memory usage Tested only with two raspberries using NRF24L01+ 10 meters apart. - The lib is capable to detect if the message payload is bigger than frame_size-sizeof(Header) and split the payload into chucks and send them separately. - This commit introduces two new flag types: - NETWORK_MORE_FRAGMENTS - NETWORK_LAST_FRAGMENT - As of IPv4 fragmentation this flags are used to mark the frames to notify the receiver if more fragments will come or this is the last fragment. - The previous _write() function to transmit the frames is used. - Changes in payload size management were made to allow dynamic payload sizes without the need to define a field in the header. - Introduce a new struct type "RF24NetworkFrame" for better frame handling inside the lib. This type encapsulates the header, the current size of the payload and a (fixed size) array containing the payload. - The frame_buffer data structure was changes in favor of a std::queue for better handling. - Make some changes renaming variables and defining the values. - Use a std::map with double key (header.to_from,header.id) to identify the incoming fragments, allowing the reassembly of multiple transmission simultaneously. - The reassembly of frames happens in the enqueue(frame) function. Based on the fragment_id and header.id the fragments are handled differently and in the end the completely assembled frames are queued in the frame_queue presenting them to the calling application. - Introduce a new debug flag to print fragmentation debug messages. - Use definitions for static values like frame size or payload max size. - Strip trailing spaces and fix some indentation issues. --- RPi/RF24Network/RF24Network.cpp | 149 ++++++++++++++++++--------- RPi/RF24Network/RF24Network.h | 111 ++++++-------------- RPi/RF24Network/RF24Network_config.h | 9 +- 3 files changed, 138 insertions(+), 131 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 9602eb73..0f248de4 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -32,7 +32,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE) {} /******************************************************************/ @@ -53,6 +53,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); radio.enableDynamicAck(); + radio.enableDynamicPayloads(); //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; @@ -110,16 +111,20 @@ uint8_t RF24Network::update(void) memcpy(&header,frame_buffer,sizeof(RF24NetworkHeader)); IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf("%u: NET message %04x\n\r",millis(),*i)); + if (len) { + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),len);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + } // Throw it away if it's not a valid address if ( !is_valid_address(header.to_node) ){ continue; } + IF_SERIAL_DEBUG(printf_P("%u: MAC Valid frame from %i with size %i received.\n\r",millis(),header.from_node,len)); + // Build the full frame - size_t payload_size = len-sizeof(RF24NetworkHeader); - RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),payload_size); + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),len-sizeof(RF24NetworkHeader)); uint8_t res = header.type; // Is this for us? @@ -186,27 +191,30 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - if (frame.header.fragment_id > 0) { - IF_SERIAL_DEBUG(printf("%u: NET message as fragment %i received\n\r",millis(),header.fragment_id);); - - //if fragment_id > 1 then - if (frame.header.fragment_id > 1) { - //Enqueue the frame with it fragmented payload in the LRUCache - //The cache will assemble all payloads into one big payload - //!frameAssemblyCache.put(frame.header.from_node,frame); - result = true; - } else if (frame.header.fragment_id == 1) { - //if we got a fragment_id == 1, then we got the last fragment - //Enqueue this frame into the frame_queue - //!frame_queue.push( frameAssemblyCache.get(frame.header.from_node) ); - result = true; - } else { - // otherwise discard the message with the large payload - result = false; - } + if (frame.header.fragment_id > 1 && frame.header.type == NETWORK_MORE_FRAGMENTS) { + //Set the more fragments flag to indicate a fragmented frame + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + + //Append payload + appendFragmentToFrame(frame); + result = true; + + } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { + //Set the last fragment flag to indicate the last fragment + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + + //Append payload + appendFragmentToFrame(frame); + + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); + //Push the assembled frame in the frame_queue and remove it from cache + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + + result = true; } else { - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.remain())); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); // Copy the current frame into the frame queue frame_queue.push(frame); @@ -224,10 +232,31 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { /******************************************************************/ +void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { + + if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { + //This is the first of many fragments + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + } else { + //We have at least received one fragments. + + //Append payload + RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); + + //Increment message size + f->message_size += frame.message_size; + //Update header + f->header = frame.header; + } +} + +/******************************************************************/ + bool RF24Network::available(void) { // Are there frames on the queue for us? - return (!frame_queue.isEmpty()); + return (!frame_queue.empty()); } /******************************************************************/ @@ -259,17 +288,20 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { - RF24NetworkFrame frame = frame_queue.pop(); + RF24NetworkFrame frame = frame_queue.front(); // How much buffer size should we actually copy? - bufsize = std::min(frame.getPayloadSize(),maxlen); + bufsize = std::min(frame.message_size,maxlen); memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); - memcpy(message,frame.getPayloadArray(),bufsize); + memcpy(message,frame.message_buffer,bufsize); - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Received %s\n\r"),millis(),header.toString())); - } + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message size %i\n",millis(),frame.message_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET readed %s\n\r"),millis(),header.toString())); + frame_queue.pop(); + } return bufsize; } @@ -309,25 +341,33 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le bool txSuccess = true; //Check payload size + if (len > MAX_PAYLOAD_SIZE) { + IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size of %i\n\r",millis(),MAX_PAYLOAD_SIZE);); + return false; + } + + //If message payload length fits in a single message + //then use the normal _write() function if (len <= max_frame_payload_size) { - //If message payload length fits in a single message - //then use the normal _write() function return _write(header,message,len,writeDirect); } - if (len >= 255*max_frame_payload_size) { - //If the message payload is too big, whe cannot generate enough fragments - //and enumerate them + //If the message payload is too big, whe cannot generate enough fragments + //and enumerate them + if (len > 255*max_frame_payload_size) { + txSuccess = false; return txSuccess; } - //The payload is smaller than 6kBytes. We cann transmit it. + + //The payload is smaller than MAX_PAYLOAD_SIZE and we can enumerate the fragments. + // --> We cann transmit the message. //Divide the message payload into chuncks of max_frame_payload_size uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) uint8_t msgCount = 0; - IF_SERIAL_DEBUG(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); //Iterate over the payload chuncks // Assemble a new message, copy and fill out the header @@ -338,34 +378,38 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le while (fragment_id > 0) { //Copy and fill out the header - RF24NetworkHeader fragmentHeader; - fragmentHeader = header; + RF24NetworkHeader fragmentHeader = header; fragmentHeader.fragment_id = fragment_id; + if (fragment_id == 1) { + fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + } else { + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } + size_t offset = msgCount*max_frame_payload_size; + size_t fragmentLen = std::min(len-offset,max_frame_payload_size); - IF_SERIAL_DEBUG(printf("%u: NET try to transmit msg with fragment '%i'\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header - bool ok = _write(fragmentHeader,message+offset,len-offset,writeDirect); + bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); if (!ok) { - IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); txSuccess = false; break; } - IF_SERIAL_DEBUG(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),(msgCount+1));); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); //Message was successful sent //Check and modify counters fragment_id--; msgCount++; - - IF_SERIAL_DEBUG(printf("%u: NET DEBUG: fragmentID=%i msgCount=%i loop_done?=%i\n\r",millis(),fragment_id,msgCount,!(fragment_id > 0));); } //Return true if all the chuncks where sent successfuly //else return false - IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. Status",millis(),msgCount); printf("%s\n\r", ok ? "ok" : "not ok");); + IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return txSuccess; } /******************************************************************/ @@ -376,21 +420,26 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l header.from_node = node_address; // Build the full frame to send - if (len) - memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + frame_size = sizeof(RF24NetworkHeader); //Set the current frame size + if (len) { + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); + frame_size += len; //Set the current frame size + } IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); - if (len) + if (frame_size) { // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG(printf("%u: NET message ",millis());const char* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf("\n\r") ); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),frame_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } // If the user is trying to send it to himself if ( header.to_node == node_address ){ // Build the frame to send - RF24NetworkFrame frame = RF24NetworkFrame(header,message,sizeof(message)); + RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); // Just queue it in the received queue return enqueue(frame); }else{ diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index e28454c1..c0d16fd4 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -25,17 +25,23 @@ #include #include #include -//!#include -#include +#include +#include // std::pair +#include #include "RF24Network_config.h" -#include "CircularBuffer.h" -#include "FrameLRUCache.h" #define MAX_FRAME_SIZE 32 #define MAX_FRAME_BUFFER_SIZE 255 -#define MAX_PAYLOAD_SIZE ((MAX_FRAME_SIZE-sizeof(RF24NetworkHeader))*255) +#define MAX_PAYLOAD_SIZE 1500 #define MAX_LRU_CACHE_SIZE 32 +//Network header message types +#define NETWORK_ACK_REQUEST 128 +#define NETWORK_ACK 129 +#define NETWORK_MORE_FRAGMENTS 130 +#define NETWORK_LAST_FRAGMENT 131 + + class RF24; /** @@ -96,8 +102,8 @@ struct RF24NetworkHeader struct RF24NetworkFrame { RF24NetworkHeader header; /**< Header which is sent with each message */ - size_t payload_size; /**< The size in bytes of the payload length */ - std::list payload_buffer; /**< Vector to put the frame payload that will be sent/received over the air */ + size_t message_size; /**< The size in bytes of the payload length */ + uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air */ /** * Default constructor @@ -110,39 +116,24 @@ struct RF24NetworkFrame * Send constructor * * Use this constructor to create a frame with header and payload and then send a message - * - * @code - * RF24NetworkFrame frame(recipient_address,'t',message,sizeof(message)); - * network.write(frame); - * @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. - * @param _payload The message content. - * @param _psize Length in bytes of the payload. */ - RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _payload = NULL, size_t _psize = 0) : header(RF24NetworkHeader(_to,_type)), payload_size(_psize) { - appendToPayload(_payload,payload_size); + RF24NetworkFrame(uint16_t _to, unsigned char _type = 0, const void* _message = NULL, size_t _len = 0) : + header(RF24NetworkHeader(_to,_type)), message_size(_len) { + if (_message && _len) { + memcpy(message_buffer,_message,_len); + } } /** * Send constructor * * Use this constructor to create a frame with header and payload and then send a message - * - * @code - * RF24NetworkHeader header(recipient_address,'t'); - * RF24NetworkFrame frame(header,message,sizeof(message)); - * network.write(frame); - * @endcode - * - * @param _header The header struct of the frame - * @param _payload The message content. - * @param _psize Length in bytes of the payload. */ - RF24NetworkFrame(RF24NetworkHeader& _header, const void* _payload = NULL, size_t _psize = 0) : header(_header), payload_size(_psize) { - appendToPayload(_payload,payload_size); + RF24NetworkFrame(RF24NetworkHeader& _header, const void* _message = NULL, size_t _len = 0) : + header(_header), message_size(_len) { + if (_message && _len) { + memcpy(message_buffer,_message,_len); + } } /** @@ -156,47 +147,6 @@ struct RF24NetworkFrame */ const char* toString(void) const; - /** - * Get the frame payload (message) as array - * - * Internally is the payload a vector. - * @return array pointer to the payload - */ - const void* getPayloadArray(void) { - //!const void* res = &payload_buffer[0]; - //!return res; - uint8_t *arr = new uint8_t[payload_buffer.size()]; - std::copy(payload_buffer.begin(),payload_buffer.end(),arr); - return arr; - } - - /** - * Get the payload size - * - * @return Size in bytes of the current payload - */ - size_t getPayloadSize(void) { - return payload_buffer.size(); - } - - /** - * Append the given payload to the current payload_buffer - * - */ - void appendToPayload(const void* payload, size_t len) { - if (payload && len) { - if ((payload_buffer.size()+len) <= MAX_PAYLOAD_SIZE) { - //Cast the payload as uint8_t - const uint8_t *q = (const uint8_t *)payload; - //Append the payload to the buffer - payload_buffer.insert(payload_buffer.end(), q, q+len); - //Save space in RAM - //!payload_buffer.shrink_to_fit(); - } - } - //Set the new payload_buffer size - payload_size = payload_buffer.size(); - }; }; /** @@ -373,6 +323,7 @@ class RF24Network uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + void appendFragmentToFrame(RF24NetworkFrame frame); private: #if defined (RF24NetworkMulticast) @@ -381,17 +332,17 @@ class RF24Network #endif RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ - const static unsigned int frame_size = MAX_FRAME_SIZE; /**< How large is each frame over the air */ - const static unsigned int max_frame_payload_size = frame_size-sizeof(RF24NetworkHeader); - uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ - CircularBuffer frame_queue; /**< RPi can buffer 500 frames (16kB) - Arduino does 5 by default. Space for a small set of frames that need to be delivered to the app layer */ - //!FrameLRUCache frameAssemblyCache; + uint8_t frame_size; /**< How large is each frame over the air */ + const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); + uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ + std::queue frame_queue; + std::map, RF24NetworkFrame> frameFragmentsCache; + 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 */ - #define NETWORK_ACK_REQUEST 128 - #define NETWORK_ACK 129 + }; /** diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 693e6715..fd3de785 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -15,8 +15,9 @@ /********** USER CONFIG **************/ //#define RF24NetworkMulticast -//#define SERIAL_DEBUG //Change #undef to #define for debug +#define SERIAL_DEBUG //Change #undef to #define for debug #define SERIAL_DEBUG_ROUTING +//#define SERIAL_DEBUG_FRAGMENTATION /*************************************/ @@ -35,6 +36,12 @@ #define IF_SERIAL_DEBUG(x) #endif +#if defined (SERIAL_DEBUG_FRAGMENTATION) +#define IF_SERIAL_DEBUG_FRAGMENTATION(x) ({x;}) +#else +#define IF_SERIAL_DEBUG_FRAGMENTATION(x) +#endif + // Avoid spurious warnings #if ! defined( NATIVE ) && defined( ARDUINO ) #undef PROGMEM From f45441532f2014a990e12d2e565e7b0a8a51baab Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 5 Sep 2014 17:29:57 +0200 Subject: [PATCH 015/105] Remove not needed CircularBuffer data structure --- RPi/RF24Network/CircularBuffer.h | 78 -------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 RPi/RF24Network/CircularBuffer.h diff --git a/RPi/RF24Network/CircularBuffer.h b/RPi/RF24Network/CircularBuffer.h deleted file mode 100644 index 22d3c236..00000000 --- a/RPi/RF24Network/CircularBuffer.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - CircularBuffer.h - circular buffer library for Arduino. - - Copyright (c) 2009 Hiroki Yagita. - Copyright (c) 2014 Rei . - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - 'Software'), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef CIRCULARBUFFER_h -#define CIRCULARBUFFER_h -#include - -template -class CircularBuffer { -public: - enum { - Empty = 0, - Half = Size / 2, - Full = Size, - }; - - CircularBuffer() : - wp_(buf_), rp_(buf_), tail_(buf_+Size), remain_(0), size_(Size) {} - ~CircularBuffer() {} - void push(T value) { - *wp_++ = value; - remain_++; - if (wp_ == tail_) wp_ = buf_; - } - T pop() { - T result = *rp_++; - remain_--; - if (rp_ == tail_) rp_ = buf_; - return result; - } - T front() { - T result = *rp_; - return result; - } - int remain() const { - return remain_; - } - bool isEmpty() { - if (remain_ > 0) - return true; - return false; - } - bool isFull() { - return (size_ == remain_); - } - -private: - T buf_[Size]; - T *wp_; - T *rp_; - T *tail_; - uint16_t remain_; - uint16_t size_; -}; - -#endif From 1b0f3c9f22b08d75d7380c11656fde0a135f99d0 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 6 Sep 2014 23:46:39 -0600 Subject: [PATCH 016/105] Fix for ATTiny in IDE 1.5.7 --- RF24Network_config.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RF24Network_config.h b/RF24Network_config.h index d8399e8a..d8b01181 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -30,7 +30,9 @@ // Define _BV for non-Arduino platforms and for Arduino DUE #if defined (ARDUINO) && !defined (__arm__) - #include + #if !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) + #include + #endif #else #include From e34a39472ba06ecf867aace81be4098d29c878d8 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 9 Sep 2014 23:26:53 -0600 Subject: [PATCH 017/105] Fixes/Workarounds for fragmentation support Fixes for https://github.com/TMRh20/RF24Network/pull/13 - modify txTimeout value - detect 0 payload length due to corrupt dynamic payload - detect and clear un-finished fragmented payloads - detect and clear frames that exceed MAX_PAYLOAD_SIZE (due to duplicate payloads, etc) - add auto-adjusted delay after writes to account for timing issues - adjust includes in examples to prevent compile errors --- RPi/RF24Network/RF24Network.cpp | 50 ++++++++++++++-------- RPi/RF24Network/RF24Network_config.h | 4 +- RPi/RF24Network/examples/helloworld_rx.cpp | 9 ++-- RPi/RF24Network/examples/helloworld_tx.cpp | 8 ++-- RPi/RF24Network/examples/rx-test.cpp | 6 ++- 5 files changed, 46 insertions(+), 31 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 0f248de4..ab335fec 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -58,8 +58,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); - txTimeout = 20; - routeTimeout = txTimeout+35; + txTimeout = 25; + routeTimeout = txTimeout*9; // Setup our address helper cache setup_address(); @@ -97,8 +97,9 @@ uint8_t RF24Network::update(void) //while (radio.available()) //{ - // Fetch the payload, and see if this was the last one. + // Fetch the payload, and see if this was the last one. size_t len = radio.getDynamicPayloadSize(); + if(len == 0){ /*printf("bad payload dropped\n");*/continue; } radio.read( frame_buffer, len ); //Do we have a valid length for a frame? @@ -112,8 +113,8 @@ uint8_t RF24Network::update(void) IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); if (len) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),len);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: Rcv MAC frame size %i\n",millis(),len);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: Rcv MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } // Throw it away if it's not a valid address @@ -199,16 +200,16 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { appendFragmentToFrame(frame); result = true; - } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { + } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { //Set the last fragment flag to indicate the last fragment IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); //Append payload - appendFragmentToFrame(frame); - + appendFragmentToFrame(frame); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); //Push the assembled frame in the frame_queue and remove it from cache - frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); result = true; @@ -235,19 +236,34 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { + + //If there is an un-finished fragment: + for (std::map, RF24NetworkFrame>::iterator it=frameFragmentsCache.begin(); it!=frameFragmentsCache.end(); ++it){ + if( it->first.first == frame.header.from_node){ + frameFragmentsCache.erase( it ); + //printf("Map Size: %d\n",frameFragmentsCache.size()); + break; + } + } + //This is the first of many fragments frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + } else { //We have at least received one fragments. - //Append payload RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + + if(frame.message_size + f->message_size > MAX_PAYLOAD_SIZE){ + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + printf("cleared corrupt frame\n"); + }else{ memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); - //Increment message size f->message_size += frame.message_size; //Update header f->header = frame.header; + } } } @@ -406,7 +422,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le fragment_id--; msgCount++; } - + int frag_delay = int(len/16); delay( std::min(frag_delay,15) ); //Return true if all the chuncks where sent successfuly //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); @@ -431,7 +447,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l if (frame_size) { // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i",millis(),frame_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i\n",millis(),frame_size);); IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } @@ -504,9 +520,9 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) } - //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 3 || directTo == 4){ - // multicast = 1; - // } + // if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK ){ + // multicast = 1; + //} IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); @@ -543,7 +559,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Now, continue listening radio.startListening(); - if( (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ + if( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index fd3de785..b276269a 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -10,12 +10,12 @@ #ifndef __RF24_CONFIG_H__ #define __RF24_CONFIG_H__ -#include +//#include /********** USER CONFIG **************/ //#define RF24NetworkMulticast -#define SERIAL_DEBUG //Change #undef to #define for debug +//#define SERIAL_DEBUG //Change #undef to #define for debug #define SERIAL_DEBUG_ROUTING //#define SERIAL_DEBUG_FRAGMENTATION diff --git a/RPi/RF24Network/examples/helloworld_rx.cpp b/RPi/RF24Network/examples/helloworld_rx.cpp index 4f0228a7..c4583d3c 100644 --- a/RPi/RF24Network/examples/helloworld_rx.cpp +++ b/RPi/RF24Network/examples/helloworld_rx.cpp @@ -9,16 +9,14 @@ * Listens for messages from the transmitter and prints them out. */ - - -#include -#include #include #include +#include #include #include #include + /** * g++ -L/usr/lib main.cc -I/usr/include -o main -lrrd **/ @@ -76,7 +74,8 @@ int main(int argc, char** argv) printf("Received payload # %lu at %lu \n",payload.counter,payload.ms); } - sleep(2); + //sleep(2); + delay(2000); //fclose(pFile); } diff --git a/RPi/RF24Network/examples/helloworld_tx.cpp b/RPi/RF24Network/examples/helloworld_tx.cpp index c1c2f9df..dbf88edc 100644 --- a/RPi/RF24Network/examples/helloworld_tx.cpp +++ b/RPi/RF24Network/examples/helloworld_tx.cpp @@ -9,12 +9,10 @@ * Listens for messages from the transmitter and prints them out. */ - - -#include -#include +//#include #include #include +#include #include #include #include @@ -33,7 +31,7 @@ using namespace std; //RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); // Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS1, BCM2835_SPI_SPEED_8MHZ); +RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); RF24Network network(radio); diff --git a/RPi/RF24Network/examples/rx-test.cpp b/RPi/RF24Network/examples/rx-test.cpp index aca057d6..d192d978 100644 --- a/RPi/RF24Network/examples/rx-test.cpp +++ b/RPi/RF24Network/examples/rx-test.cpp @@ -1,7 +1,9 @@ -#include -#include + + + #include #include +#include #include #include /*#include */ From cf2c29f37a8cda5cad60c4134141a9714260b81d Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 10 Sep 2014 01:36:04 -0600 Subject: [PATCH 018/105] Arduino support for routing fragmented payloads - Arduino enableDynamicPayloads() - Added RF24Network support for routing of fragmented payloads on Arduino ( Only fully supported on RPi currently) - Added delay after detecting corrupt dynamic payloads --- RF24Network.cpp | 25 ++++++++++++++++--------- RPi/RF24Network/RF24Network.cpp | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 784f98ee..d0ad310c 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -24,6 +24,7 @@ uint16_t levelToAddress( uint8_t level ); #endif bool is_valid_address( uint16_t node ); uint32_t nFails = 0, nOK=0; +uint8_t dynLen = 0; /******************************************************************/ #if !defined (DUAL_HEAD_RADIO) @@ -53,11 +54,12 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); radio.enableDynamicAck(); + radio.enableDynamicPayloads(); // Use different retry periods to reduce data collisions uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); - txTimeout = 25; + txTimeout = 30; routeTimeout = txTimeout*9; // Adjust for max delay per node //printf_P(PSTR("Retries: %d, txTimeout: %d"),retryVar,txTimeout); @@ -102,7 +104,7 @@ uint8_t RF24Network::update(void) { // Dump the payloads until we've gotten everything - + //while (radio.available()) //{ // Fetch the payload, and see if this was the last one. @@ -124,7 +126,7 @@ uint8_t RF24Network::update(void) if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd \n")); + //printf_P(PSTR("MAC: Network ACK Rcvd \n")); #endif return NETWORK_ACK; } // Add it to the buffer of frames for us @@ -133,7 +135,8 @@ uint8_t RF24Network::update(void) }else{ - + dynLen = radio.getDynamicPayloadSize(); + if(!dynLen){continue;} #if defined (RF24NetworkMulticast) if( header.to_node == 0100){ if(header.id != lastMultiMessageID){ @@ -383,9 +386,9 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F } - //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK || directTo == 4){ + //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK ){ // multicast = 0; - // } + //} // printf("Multi %d\n\r",multicast); IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); @@ -401,11 +404,14 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address){ frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK + //frame_buffer[7] = 0; frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address + dynLen=8; write(fromAddress,1); // Send it back as a routed message + dynLen=0; #if defined (SERIAL_DEBUG_ROUTING) //if(!test){ - printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),send_node,fromAddress); + //printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),send_node,fromAddress); //}else{ //printf("ACK send fail"); //} @@ -433,7 +439,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F radio.startListening(); #endif - if( (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ + if( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ @@ -469,7 +475,8 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // First, stop listening so we can talk radio.stopListening(); radio.openWritingPipe(out_pipe); - radio.writeFast(frame_buffer, frame_size,multicast); + size_t wLen = dynLen ? dynLen: frame_size; + radio.writeFast(frame_buffer, wLen,multicast); ok = radio.txStandBy(txTimeout); #else diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index ab335fec..e885068b 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -97,9 +97,9 @@ uint8_t RF24Network::update(void) //while (radio.available()) //{ - // Fetch the payload, and see if this was the last one. + // Fetch the payload, and get the size size_t len = radio.getDynamicPayloadSize(); - if(len == 0){ /*printf("bad payload dropped\n");*/continue; } + if(len == 0){ delay(2);/*printf("bad payload dropped\n");*/continue; } radio.read( frame_buffer, len ); //Do we have a valid length for a frame? From a4b60763ed03d1a125a79b8a4183361874c01dc0 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 11 Sep 2014 01:09:42 -0600 Subject: [PATCH 019/105] Per #14 add first/last fragment, debugging cleanup Per #14 and https://github.com/TMRh20/RF24Network/commit/e34a39472ba06ecf867aace81be4098d29c878d8#commitcomment-7727886 added NETWORK_FIRST_FRAGMENT - Add total_fragments to RF24NetworkFrame - Add error checking per discussion - Modify debug to include FRG for fragment handling messages and RT for routed messages - Add SERIAL_DEBUG_MINIMAL to be changed as required for debugging new features in RF24NetworkDEV --- RPi/RF24Network/RF24Network.cpp | 113 +++++++++++++++++---------- RPi/RF24Network/RF24Network.h | 12 +-- RPi/RF24Network/RF24Network_config.h | 11 ++- 3 files changed, 87 insertions(+), 49 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index e885068b..13233eb1 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -113,8 +113,8 @@ uint8_t RF24Network::update(void) IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); if (len) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: Rcv MAC frame size %i\n",millis(),len);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: Rcv MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame size %i\n",millis(),len);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } // Throw it away if it's not a valid address @@ -122,7 +122,7 @@ uint8_t RF24Network::update(void) continue; } - IF_SERIAL_DEBUG(printf_P("%u: MAC Valid frame from %i with size %i received.\n\r",millis(),header.from_node,len)); + IF_SERIAL_DEBUG(printf_P("%u: MAC Valid payload from %i with size %i received.\n\r",millis(),header.from_node,len)); // Build the full frame RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),len-sizeof(RF24NetworkHeader)); @@ -132,7 +132,7 @@ uint8_t RF24Network::update(void) if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd\n")); + printf_P(PSTR("RT: Network ACK Rcvd\n")); #endif return NETWORK_ACK; } @@ -146,7 +146,7 @@ uint8_t RF24Network::update(void) if(header.id != lastMultiMessageID){ if(multicastRelay){ #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); + printf_P(PSTR("RT: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); #endif write(levelToAddress(multicast_level)<<3,4); } @@ -155,7 +155,7 @@ uint8_t RF24Network::update(void) } #ifdef SERIAL_DEBUG_ROUTING else{ - printf_P(PSTR("MAC: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); + printf_P(PSTR("RT: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); } #endif }else{ @@ -192,9 +192,10 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - if (frame.header.fragment_id > 1 && frame.header.type == NETWORK_MORE_FRAGMENTS) { + + if (frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)){ //Set the more fragments flag to indicate a fragmented frame - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); //Append payload appendFragmentToFrame(frame); @@ -202,25 +203,34 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { //Set the last fragment flag to indicate the last fragment - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); - + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + //Append payload appendFragmentToFrame(frame); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); - //Push the assembled frame in the frame_queue and remove it from cache - frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + + //Push the assembled frame in the frame_queue and remove it from cache + // If total_fragments == 0, we are finished + if(!frame.total_fragments){ + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + }else{ + IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); + } frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); result = true; - } else { + } else if(frame.header.type < 128){ IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); // Copy the current frame into the frame queue frame_queue.push(frame); result = true; + }else{ + IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d \n",millis(),frame.header.type); ); } + if (result) { IF_SERIAL_DEBUG(printf("ok\n\r")); @@ -234,36 +244,48 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { /******************************************************************/ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { - + if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { - //If there is an un-finished fragment: - for (std::map, RF24NetworkFrame>::iterator it=frameFragmentsCache.begin(); it!=frameFragmentsCache.end(); ++it){ - if( it->first.first == frame.header.from_node){ - frameFragmentsCache.erase( it ); - //printf("Map Size: %d\n",frameFragmentsCache.size()); - break; - } - } - - //This is the first of many fragments - frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + // If this is the first of many fragments. + if(frame.header.type == NETWORK_FIRST_FRAGMENT){ + // Decrement the stored total_fragments counter + frame.total_fragments = frame.header.fragment_id-1; + // Cache the fragment + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + }else{ + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Dropping all fragments for frame id:%d, missing first fragment(s).\n",millis(),frame.header.id);); + } } else { //We have at least received one fragments. //Append payload RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + //Error checking for missed fragments and payload size + if(frame.header.fragment_id != f->header.fragment_id-1){ + if(frame.header.fragment_id > f->header.fragment_id){ + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + }else{ + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id-1);); + } + return; + } if(frame.message_size + f->message_size > MAX_PAYLOAD_SIZE){ frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - printf("cleared corrupt frame\n"); - }else{ + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %d exceeds MAX_PAYLOAD_SIZE %d Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); + return; + } + memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); //Increment message size f->message_size += frame.message_size; //Update header f->header = frame.header; - } + //Update Total fragments + f->total_fragments--; + } } @@ -312,9 +334,9 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); memcpy(message,frame.message_buffer,bufsize); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message size %i\n",millis(),frame.message_size);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET readed %s\n\r"),millis(),header.toString())); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message size %i\n",millis(),frame.message_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET read %s\n\r"),millis(),header.toString())); frame_queue.pop(); } @@ -358,7 +380,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Check payload size if (len > MAX_PAYLOAD_SIZE) { - IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size of %i\n\r",millis(),MAX_PAYLOAD_SIZE);); + IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size %i\n\r",millis(),MAX_PAYLOAD_SIZE);); return false; } @@ -383,8 +405,9 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) uint8_t msgCount = 0; - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET total message fragments %i\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); + bool firstFrag = 1; //Iterate over the payload chuncks // Assemble a new message, copy and fill out the header // Try to send this message @@ -400,29 +423,35 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (fragment_id == 1) { fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment } else { - fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + if(firstFrag == 1){ + fragmentHeader.type = NETWORK_FIRST_FRAGMENT; + firstFrag = 0; + }else{ + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } } size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = std::min(len-offset,max_frame_payload_size); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); if (!ok) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); txSuccess = false; break; } - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: NET message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); //Message was successful sent //Check and modify counters fragment_id--; msgCount++; } - int frag_delay = int(len/16); delay( std::min(frag_delay,15) ); + int frag_delay = int(len/32); + delay( std::min(frag_delay,15) ); //Return true if all the chuncks where sent successfuly //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); @@ -447,8 +476,8 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l if (frame_size) { // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame size %i\n",millis(),frame_size);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: MAC frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG frame size %i\n",millis(),frame_size);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } @@ -540,7 +569,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; write(fromAddress,1); #if defined (SERIAL_DEBUG_ROUTING) - printf("MAC: Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); + printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); #endif } @@ -565,7 +594,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) if(millis() - reply_time > routeTimeout){ ok=0; #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%u: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); + printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); #endif break; } diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index c0d16fd4..8400a726 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -38,8 +38,10 @@ //Network header message types #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 -#define NETWORK_MORE_FRAGMENTS 130 -#define NETWORK_LAST_FRAGMENT 131 +#define NETWORK_FIRST_FRAGMENT 130 +#define NETWORK_MORE_FRAGMENTS 131 +#define NETWORK_LAST_FRAGMENT 132 + class RF24; @@ -104,7 +106,7 @@ struct RF24NetworkFrame RF24NetworkHeader header; /**< Header which is sent with each message */ size_t message_size; /**< The size in bytes of the payload length */ uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air */ - + uint8_t total_fragments; /** Date: Thu, 11 Sep 2014 02:56:30 -0600 Subject: [PATCH 020/105] #15 Fix user-defined header types w/fragmentation Since a fragment_id and fragment type is sent, we can include and restore the user-defined header type with fragmented payloads by including it with the final fragment, since we know its expected fragment_id and that this should be the last expected fragment. --- RPi/RF24Network/RF24Network.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 13233eb1..b8a277cf 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -193,15 +193,15 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - if (frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)){ + if ((frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ){ //Set the more fragments flag to indicate a fragmented frame IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); //Append payload appendFragmentToFrame(frame); - result = true; - - } else if (frame.header.fragment_id == 1 && frame.header.type == NETWORK_LAST_FRAGMENT) { + result = true; + + }else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { //Set the last fragment flag to indicate the last fragment IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); @@ -262,6 +262,10 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { //Append payload RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + if(frame.header.type == NETWORK_LAST_FRAGMENT){ + f->header.type = frame.header.fragment_id; + frame.header.fragment_id = 1; + } //Error checking for missed fragments and payload size if(frame.header.fragment_id != f->header.fragment_id-1){ if(frame.header.fragment_id > f->header.fragment_id){ @@ -407,7 +411,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); - bool firstFrag = 1; + //Iterate over the payload chuncks // Assemble a new message, copy and fill out the header // Try to send this message @@ -422,10 +426,10 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (fragment_id == 1) { fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + fragmentHeader.fragment_id = header.type; } else { - if(firstFrag == 1){ + if(msgCount == 0){ fragmentHeader.type = NETWORK_FIRST_FRAGMENT; - firstFrag = 0; }else{ fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } From 14db5e34052cc0ab8f7b6f634f9c0724c130757f Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 13 Sep 2014 07:49:23 -0600 Subject: [PATCH 021/105] #15 #14 Improve fragmentation and transfer speed Notes: - May require updating the RF24 driver (radio.rxFifoFull()) - Continue buffering data if a Network Ack is received and there is data in the FIFO - Fragmented payload handling improvements - Repair and tested non-fragmented and fragmented data transfers with user-defined header types - Handling for data sent to self - Stop listening during fragmented data transfers, speeds up data rates (more to come) - Adjusted frag_delay. - Thanks to https://github.com/reixd and rRF24toTUN for making testing things a lot easier --- RPi/RF24Network/RF24Network.cpp | 184 +++++++++++++++++++++++++------- RPi/RF24Network/RF24Network.h | 21 +++- 2 files changed, 160 insertions(+), 45 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index b8a277cf..0ca14379 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -58,7 +58,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); - txTimeout = 25; + txTimeout = 30; routeTimeout = txTimeout*9; // Setup our address helper cache @@ -91,7 +91,13 @@ uint8_t RF24Network::update(void) { // if there is data ready uint8_t pipe_num; + uint8_t returnVal = 0; + + #if defined (SERIAL_DEBUG) while ( radio.isValid() && radio.available(&pipe_num) ) + #else + while ( radio.isValid() && radio.available() ) + #endif { // Dump the payloads until we've gotten everything @@ -99,7 +105,7 @@ uint8_t RF24Network::update(void) //{ // Fetch the payload, and get the size size_t len = radio.getDynamicPayloadSize(); - if(len == 0){ delay(2);/*printf("bad payload dropped\n");*/continue; } + if(len == 0){ delay(2);printf("Bad Dyn Payload\n");continue; } radio.read( frame_buffer, len ); //Do we have a valid length for a frame? @@ -134,9 +140,23 @@ uint8_t RF24Network::update(void) #ifdef SERIAL_DEBUG_ROUTING printf_P(PSTR("RT: Network ACK Rcvd\n")); #endif - return NETWORK_ACK; + returnVal = NETWORK_ACK; + continue; } - enqueue(frame); + + enqueue(frame); + + if(radio.rxFifoFull()){ + //if(millis() - timeout > 1){ + printf(" fifo full\n"); + //break; + //} + } + //if(res == NETWORK_FIRST_FRAGMENT || res == NETWORK_MORE_FRAGMENTS){ + // uint32_t timeout = millis(); + // + //} + }else{ @@ -184,7 +204,7 @@ uint8_t RF24Network::update(void) #endif //} } - return 0; + return returnVal; } /******************************************************************/ @@ -192,11 +212,17 @@ uint8_t RF24Network::update(void) bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; - + // This is sent to itself + if(frame.header.from_node == node_address){ + bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); + if(isFragment){ printf("Cannot enqueue multi-payload frames to self\n"); return false; } + frame_queue.push(frame); + //printf("self\n"); + return true; + } if ((frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ){ //Set the more fragments flag to indicate a fragmented frame IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); - //Append payload appendFragmentToFrame(frame); result = true; @@ -204,7 +230,6 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { }else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { //Set the last fragment flag to indicate the last fragment IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); - //Append payload appendFragmentToFrame(frame); @@ -212,9 +237,10 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { //Push the assembled frame in the frame_queue and remove it from cache // If total_fragments == 0, we are finished - if(!frame.total_fragments){ + if( frame.header.to_node == node_address || (!frame.header.fragment_id && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ){ frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); }else{ + IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); } frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); @@ -223,12 +249,12 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { } else if(frame.header.type < 128){ IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); - // Copy the current frame into the frame queue frame_queue.push(frame); result = true; }else{ - IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d \n",millis(),frame.header.type); ); + IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.fragment_id); ); + } @@ -245,34 +271,46 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { + bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); + + if(!isFragment){ + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + //printf("not frag type %d\n",frame.header.type); + }else + if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { // If this is the first of many fragments. - if(frame.header.type == NETWORK_FIRST_FRAGMENT){ + if(frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)){ // Decrement the stored total_fragments counter - frame.total_fragments = frame.header.fragment_id-1; + frame.header.fragment_id = frame.header.fragment_id-1; // Cache the fragment frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + }else{ - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Dropping all fragments for frame id:%d, missing first fragment(s).\n",millis(),frame.header.id);); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Dropping all fragments for frame with header id:%d, missing first fragment(s).\n",millis(),frame.header.id);); } } else { //We have at least received one fragments. //Append payload RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); - + if(frame.header.type == NETWORK_LAST_FRAGMENT){ - f->header.type = frame.header.fragment_id; + //Note: The type is sent with the last fragment in the fragment_id field + frame.header.type = frame.header.fragment_id; frame.header.fragment_id = 1; + //printf("frag last\n"); } //Error checking for missed fragments and payload size - if(frame.header.fragment_id != f->header.fragment_id-1){ + //if(frame.header.fragment_id != f->header.fragment_id-1){ + if(frame.header.fragment_id != f->header.fragment_id){ if(frame.header.fragment_id > f->header.fragment_id){ IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); }else{ - frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id-1);); + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); } return; } @@ -288,7 +326,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { //Update header f->header = frame.header; //Update Total fragments - f->total_fragments--; + f->header.fragment_id--; } } @@ -379,9 +417,13 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le return write(header,message,len,070); } /******************************************************************/ + +bool noListen = 1; + bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ bool txSuccess = true; - + + //printf("x\n"); //Check payload size if (len > MAX_PAYLOAD_SIZE) { IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size %i\n\r",millis(),MAX_PAYLOAD_SIZE);); @@ -390,14 +432,14 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //If message payload length fits in a single message //then use the normal _write() function - if (len <= max_frame_payload_size) { - return _write(header,message,len,writeDirect); - } + //if (len <= max_frame_payload_size) { + // return _write(header,message,len,writeDirect); + //} //If the message payload is too big, whe cannot generate enough fragments //and enumerate them if (len > 255*max_frame_payload_size) { - + //printf("max size exceeded\n"); txSuccess = false; return txSuccess; } @@ -418,12 +460,29 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // If it fails // then break // return result as false + + radio.stopListening(); + + + //Normal Write (Un-Fragmented) + if(len <= max_frame_payload_size){ + txSuccess = _write(header,message,len,writeDirect); + radio.startListening(); + //printf("NRML write - type %d\n",header.type); + return txSuccess; + } + + noListen = 1; + + //Fragmented Write while (fragment_id > 0) { - + + //printf("fragWrite"); //Copy and fill out the header RF24NetworkHeader fragmentHeader = header; fragmentHeader.fragment_id = fragment_id; + //if(!isFragmented){ if (fragment_id == 1) { fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment fragmentHeader.fragment_id = header.type; @@ -434,7 +493,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } } - + //} size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = std::min(len-offset,max_frame_payload_size); @@ -442,20 +501,35 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Try to send the payload chunk with the copied header bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + if(!noListen){ + delayMicroseconds(50); + } if (!ok) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); - txSuccess = false; - break; + //ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + //if(!ok){ + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + txSuccess = false; + break; + //} } IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + //Message was successful sent //Check and modify counters fragment_id--; msgCount++; } - int frag_delay = int(len/32); - delay( std::min(frag_delay,15) ); + + noListen = 0; + //txSuccess = radio.txStandBy(txTimeout); + + radio.startListening(); + + int frag_delay = uint8_t(len/48); + //if(!txSuccess){ + delay( std::min(frag_delay,20)); + //} //Return true if all the chuncks where sent successfuly //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); @@ -520,7 +594,10 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // First, stop listening so we can talk. //radio.stopListening(); - + if(to_node != parent_node && !is_direct_child(to_node) ){ + noListen=0; + } + // Where do we send this? By default, to our parent uint16_t send_node = parent_node; // On which pipe @@ -539,7 +616,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { // Send directly send_node = to_node; - + // To its listening pipe send_pipe = 5; } @@ -548,6 +625,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // and let the direct child relay it. else if ( is_descendant(to_node) ) { + send_node = direct_child_route_to(to_node); send_pipe = 5; } @@ -559,7 +637,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); - + // Put the frame on the pipe ok = write_to_pipe( send_node, send_pipe, multicast ); //printf("Multi %d\n",multicast); @@ -568,6 +646,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); } #endif + if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ){ frame_buffer[6] = NETWORK_ACK; frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; @@ -576,9 +655,10 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); #endif } - - - + + if(!noListen ){ + radio.startListening(); + } // NOT NEEDED anymore. Now all reading pipes are open to start. #if 0 // If we are talking on our talking pipe, it's possible that no one is listening. @@ -589,10 +669,14 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) ok = write_to_pipe( parent_node, 0 ); #endif + // Now, continue listening - radio.startListening(); + //radio.startListening(); if( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ + if(!noListen){ + radio.startListening(); + } uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ @@ -603,6 +687,9 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) break; } } + if(!noListen){ + radio.stopListening(); + } //if(pOK){printf("pOK\n");} } if(ok == true){ @@ -613,6 +700,7 @@ return ok; } /******************************************************************/ +uint32_t lastWriteTime = 0; bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { @@ -621,13 +709,27 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) uint64_t out_pipe = pipe_address( node, pipe ); // First, stop listening so we can talk - radio.stopListening(); + //while(millis() - lastWriteTime < 4); + //lastWriteTime = millis(); + + if(!noListen){ + radio.stopListening(); + } // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); // Retry a few times + //ok = radio.write(frame_buffer, frame_size,multicast); radio.writeFast(frame_buffer, frame_size,multicast); - ok = radio.txStandBy(txTimeout); + + ok = radio.txStandBy(txTimeout); + + //if(!ok){ + //update(); + //radio.writeFast(frame_buffer, frame_size,multicast); + //ok = radio.txStandBy(0); + //} + //ok = radio.write(frame_buffer,frame_size); IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 8400a726..cb0eff26 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -35,12 +35,25 @@ #define MAX_PAYLOAD_SIZE 1500 #define MAX_LRU_CACHE_SIZE 32 -//Network header message types +/** + * Network Management message types for management of network frames and messages + * System discard types (128 to 147) Contain no user data, just additional system sub-types sent for informational purposes. (Initially within NETWORK_ACK responses) + * System retained types (148-167) Contain user data + * + * System types can also contain sub-types, included as information, TBD + * + */ + +/* System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 -#define NETWORK_FIRST_FRAGMENT 130 -#define NETWORK_MORE_FRAGMENTS 131 -#define NETWORK_LAST_FRAGMENT 132 + /*System-Sub Types (0-255)*/ + #define NETWORK_REQ_STREAM 11; + +/* System retained types */ +#define NETWORK_FIRST_FRAGMENT 148 +#define NETWORK_MORE_FRAGMENTS 149 +#define NETWORK_LAST_FRAGMENT 150 From ee420d1734b5823ca163ac4a5c46d37ecb2b5d18 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 17 Sep 2014 19:53:56 +0200 Subject: [PATCH 022/105] Cleanup code. - Remove commented code, it is bad practice. - Use a C++ indent style - Set indent witdth to 2 spaces and no tabs - Change some code to improve readability - Add more comments to the code - Define variables C++ like stlye, in the header and the constructor --- RPi/RF24Network/RF24Network.cpp | 802 +++++++++++++-------------- RPi/RF24Network/RF24Network.h | 29 +- RPi/RF24Network/RF24Network_config.h | 64 ++- 3 files changed, 425 insertions(+), 470 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 0ca14379..794cf4be 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -6,6 +6,7 @@ modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. */ + #include #include #include @@ -32,7 +33,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE), lastMultiMessageID(0), noListen(1), lastWriteTime(0) {} /******************************************************************/ @@ -55,7 +56,6 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { radio.enableDynamicAck(); radio.enableDynamicPayloads(); - //uint8_t retryVar = (node_address % 7) + 5; uint8_t retryVar = (((node_address % 6)+1) *2) + 3; radio.setRetries(retryVar, 5); txTimeout = 30; @@ -69,141 +69,111 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { while (i--) { radio.openReadingPipe(i,pipe_address(_node_address,i)); } - #if defined (RF24NetworkMulticast) - uint8_t count = 0; uint16_t addy = _node_address; - while(addy) { - addy/=8; - count++; - } - multicast_level = count; - #endif +#if defined (RF24NetworkMulticast) + uint8_t count = 0; uint16_t addy = _node_address; + while(addy) { + addy/=8; + count++; + } + multicast_level = count; +#endif radio.startListening(); - } /******************************************************************/ -void RF24Network::failures(uint32_t *_fails, uint32_t *_ok){ - *_fails = nFails; - *_ok = nOK; +void RF24Network::failures(uint32_t *_fails, uint32_t *_ok) { + *_fails = nFails; + *_ok = nOK; } -uint8_t RF24Network::update(void) -{ +uint8_t RF24Network::update(void) { // if there is data ready - uint8_t pipe_num; uint8_t returnVal = 0; - - #if defined (SERIAL_DEBUG) + +#if defined (SERIAL_DEBUG) while ( radio.isValid() && radio.available(&pipe_num) ) - #else +#else while ( radio.isValid() && radio.available() ) - #endif +#endif { - // Dump the payloads until we've gotten everything - - //while (radio.available()) - //{ - // Fetch the payload, and get the size - size_t len = radio.getDynamicPayloadSize(); - if(len == 0){ delay(2);printf("Bad Dyn Payload\n");continue; } - radio.read( frame_buffer, len ); - - //Do we have a valid length for a frame? - //We need at least a frame with header a no payload (payload_size equals 0). - if (len < sizeof(RF24NetworkHeader)) - continue; + // Fetch the payload, and get the size + size_t len = radio.getDynamicPayloadSize(); + if (len == 0) { + delay(2); + printf("Bad Dyn Payload\n"); + continue; + } + radio.read( frame_buffer, len ); + + //Do we have a valid length for a frame? + //We need at least a frame with header a no payload (payload_size equals 0). + if (len < sizeof(RF24NetworkHeader)) { + continue; + } - // Read the beginning of the frame as the header - RF24NetworkHeader header; - memcpy(&header,frame_buffer,sizeof(RF24NetworkHeader)); + // Read the beginning of the frame as the header + RF24NetworkHeader header; + memcpy(&header,frame_buffer,sizeof(RF24NetworkHeader)); - IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); - if (len) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame size %i\n",millis(),len);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); - } + IF_SERIAL_DEBUG(printf_P("%u: MAC Received on %u %s\n\r",millis(),pipe_num,header.toString())); + if (len) { + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame size %i\n",millis(),len);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Rcv frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < len; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + } - // Throw it away if it's not a valid address - if ( !is_valid_address(header.to_node) ){ + // Throw it away if it's not a valid address + if ( !is_valid_address(header.to_node) ) { + continue; + } + + IF_SERIAL_DEBUG(printf_P("%u: MAC Valid payload from %i with size %i received.\n\r",millis(),header.from_node,len)); + + // Build the full frame + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),len-sizeof(RF24NetworkHeader)); + + // Is this for us? + if ( header.to_node == node_address ) { + if (header.type == NETWORK_ACK) { + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: Network ACK Rcvd\n"));); + returnVal = NETWORK_ACK; continue; } - IF_SERIAL_DEBUG(printf_P("%u: MAC Valid payload from %i with size %i received.\n\r",millis(),header.from_node,len)); + enqueue(frame); + + if (radio.rxFifoFull()) { + printf(" fifo full\n"); + } - // Build the full frame - RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),len-sizeof(RF24NetworkHeader)); + } else { //Frame if not for us - uint8_t res = header.type; - // Is this for us? - if ( header.to_node == node_address ){ - if(res == NETWORK_ACK){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("RT: Network ACK Rcvd\n")); - #endif - returnVal = NETWORK_ACK; - continue; + #if defined (RF24NetworkMulticast) + if ( header.to_node == 0100) { + + if (header.id != lastMultiMessageID) { + if (multicastRelay) { + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1);); + write(levelToAddress(multicast_level)<<3,4); } - - enqueue(frame); - - if(radio.rxFifoFull()){ - //if(millis() - timeout > 1){ - printf(" fifo full\n"); - //break; - //} - } - //if(res == NETWORK_FIRST_FRAGMENT || res == NETWORK_MORE_FRAGMENTS){ - // uint32_t timeout = millis(); - // - //} - + lastMultiMessageID = header.id; + enqueue(frame); - }else{ + } else { + //Duplicate received + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node);); + } - #if defined (RF24NetworkMulticast) - if( header.to_node == 0100){ - if(header.id != lastMultiMessageID){ - if(multicastRelay){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("RT: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); - #endif - write(levelToAddress(multicast_level)<<3,4); - } - enqueue(frame); - lastMultiMessageID = header.id; - } - #ifdef SERIAL_DEBUG_ROUTING - else{ - printf_P(PSTR("RT: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node); - } - #endif - }else{ - write(header.to_node,1); //Send it on, indicate it is a routed payload - } - #else - //if(radio.available()){printf("------FLUSHED DATA --------------");} + } else { + write(header.to_node,1); //Send it on, indicate it is a routed payload + } + #else //Not Multicast write(header.to_node,1); //Send it on, indicate it is a routed payload - #endif - } - - // NOT NEEDED anymore. Now all reading pipes are open to start. -#if 0 - // If this was for us, from one of our children, but on our listening - // pipe, it could mean that we are not listening to them. If so, open up - // and listen to their talking pipe - - if ( header.to_node == node_address && pipe_num == 0 && is_descendant(header.from_node) ) - { - uint8_t pipe = pipe_to_descendant(header.from_node); - radio.openReadingPipe(pipe,pipe_address(node_address,pipe)); - - // Also need to open pipe 1 so the system can get the full 5-byte address of the pipe. - radio.openReadingPipe(1,pipe_address(node_address,1)); - } -#endif - //} - } + #endif + } + + } // END while radio.isValid() && radio.available() + return returnVal; } @@ -213,50 +183,62 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; // This is sent to itself - if(frame.header.from_node == node_address){ - bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); - if(isFragment){ printf("Cannot enqueue multi-payload frames to self\n"); return false; } - frame_queue.push(frame); - //printf("self\n"); - return true; - } - if ((frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ){ + if (frame.header.from_node == node_address) { + bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); + if (isFragment) { + printf("Cannot enqueue multi-payload frames to self\n"); + return false; + } + frame_queue.push(frame); + return true; + } + + if ((frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ) { + //The received frame contains the a fragmented payload + //Set the more fragments flag to indicate a fragmented frame IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + //Append payload appendFragmentToFrame(frame); - result = true; - - }else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { - //Set the last fragment flag to indicate the last fragment + result = true; + + } else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { + //The last fragment flag indicates that we received the last fragment + //Workaround/Hack: In this case the header.fragment_id does not count the number of fragments but is a copy of the type flags specified by the user application. + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); //Append payload - appendFragmentToFrame(frame); - + appendFragmentToFrame(frame); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); - //Push the assembled frame in the frame_queue and remove it from cache - // If total_fragments == 0, we are finished - if( frame.header.to_node == node_address || (!frame.header.fragment_id && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ){ - frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); - }else{ - - IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); - } + //Push the assembled frame in the frame_queue and remove it from cache + // If total_fragments == 0, we are finished + if ( frame.header.to_node == node_address || (!frame.header.fragment_id && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ) { + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + } else { + IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); + } + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); result = true; - } else if(frame.header.type < 128){ + } else if (frame.header.type < 128) { + //This is not a fragmented payload but a whole frame. + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); // Copy the current frame into the frame queue frame_queue.push(frame); result = true; - }else{ - IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.fragment_id); ); - + + } else { + //Undefined/Unknown header.type received. Drop frame! + IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.fragment_id); ); + //The frame is not explicitly dropped, but the given object is ignored. + //FIXME: does this causes problems with memory management? } - if (result) { IF_SERIAL_DEBUG(printf("ok\n\r")); @@ -270,91 +252,90 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { /******************************************************************/ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { - + bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); - - if(!isFragment){ - frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; - //printf("not frag type %d\n",frame.header.type); - }else + + if (!isFragment) { + //The received payload is not a fragment. + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + } else if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { - - // If this is the first of many fragments. - if(frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)){ - // Decrement the stored total_fragments counter - frame.header.fragment_id = frame.header.fragment_id-1; - // Cache the fragment - frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; - - }else{ - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Dropping all fragments for frame with header id:%d, missing first fragment(s).\n",millis(),frame.header.id);); - } - + // This is the first of many fragments. + + if (frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)) { + // Decrement the stored total_fragments counter + frame.header.fragment_id = frame.header.fragment_id-1; + // Cache the fragment + frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; + } else { + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Dropping all fragments for frame with header id:%d, missing first fragment(s).\n",millis(),frame.header.id);); + } + } else { //We have at least received one fragments. //Append payload + RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); - if(frame.header.type == NETWORK_LAST_FRAGMENT){ - //Note: The type is sent with the last fragment in the fragment_id field - frame.header.type = frame.header.fragment_id; - frame.header.fragment_id = 1; - //printf("frag last\n"); - } - //Error checking for missed fragments and payload size - //if(frame.header.fragment_id != f->header.fragment_id-1){ - if(frame.header.fragment_id != f->header.fragment_id){ - if(frame.header.fragment_id > f->header.fragment_id){ - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); - frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - }else{ - frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); - } - return; - } - if(frame.message_size + f->message_size > MAX_PAYLOAD_SIZE){ - frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %d exceeds MAX_PAYLOAD_SIZE %d Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); - return; - } - + if (frame.header.type == NETWORK_LAST_FRAGMENT) { + //Workaround/Hack: The user application specified header.type is sent with the last fragment in the fragment_id field + frame.header.type = frame.header.fragment_id; + frame.header.fragment_id = 1; + } + + //Error checking for missed fragments and payload size + if (frame.header.fragment_id != f->header.fragment_id) { + if (frame.header.fragment_id > f->header.fragment_id) { + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + } else { + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + } + return; + } + + //Check if the total payload exceeds the MAX_PAYLOAD_SIZE. + //If so, drop the assembled frame. + if (frame.message_size + f->message_size > MAX_PAYLOAD_SIZE) { + frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %l exceeds MAX_PAYLOAD_SIZE %l Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); + return; + } + + //Append the received fragment to the cached frame memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); - //Increment message size - f->message_size += frame.message_size; - //Update header - f->header = frame.header; - //Update Total fragments - f->header.fragment_id--; + f->message_size += frame.message_size; //Increment message size + f->header = frame.header; //Update header + f->header.fragment_id--; //Update Total fragments } } /******************************************************************/ -bool RF24Network::available(void) -{ +bool RF24Network::available(void) { // Are there frames on the queue for us? return (!frame_queue.empty()); } /******************************************************************/ -uint16_t RF24Network::parent() const -{ - if ( node_address == 0 ) +uint16_t RF24Network::parent() const { + //Return the parent node of this node. If we are the parent (master) node, return -1. + if ( node_address == 0 ) { return -1; - else + } else { return parent_node; + } } /******************************************************************/ -void RF24Network::peek(RF24NetworkHeader& header) -{ - if ( available() ) - { +void RF24Network::peek(RF24NetworkHeader& header) { + //Get an insight in the next frame in the queue. + if ( available() ) { RF24NetworkFrame frame = frame_queue.front(); memcpy(&header,&frame,sizeof(RF24NetworkHeader)); } @@ -362,12 +343,12 @@ void RF24Network::peek(RF24NetworkHeader& header) /******************************************************************/ -size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) -{ +size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) { + //Get the next queued frame. + size_t bufsize = 0; - if ( available() ) - { + if ( available() ) { RF24NetworkFrame frame = frame_queue.front(); // How much buffer size should we actually copy? @@ -382,15 +363,17 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) frame_queue.pop(); } + return bufsize; } /******************************************************************/ -#if defined RF24NetworkMulticast -bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level){ - // Fill out the header +#if defined (RF24NetworkMulticast) +bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level) { + + // Fill out the header header.to_node = 0100; header.from_node = node_address; @@ -398,102 +381,93 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); - if (len) - { + if (len) { IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); } - //uint16_t levelAddr = (level * 10)*8; uint16_t levelAddr = 1; levelAddr = levelAddr << ((level-1) * 3); return write(levelAddr,4); } -#endif +#endif //RF24NetworkMulticast /******************************************************************/ -bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ + +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len) { return write(header,message,len,070); } + /******************************************************************/ -bool noListen = 1; +bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect) { -bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ bool txSuccess = true; - - //printf("x\n"); + //Check payload size if (len > MAX_PAYLOAD_SIZE) { IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size %i\n\r",millis(),MAX_PAYLOAD_SIZE);); return false; } - //If message payload length fits in a single message - //then use the normal _write() function - //if (len <= max_frame_payload_size) { - // return _write(header,message,len,writeDirect); - //} - //If the message payload is too big, whe cannot generate enough fragments //and enumerate them if (len > 255*max_frame_payload_size) { - //printf("max size exceeded\n"); txSuccess = false; return txSuccess; } - //The payload is smaller than MAX_PAYLOAD_SIZE and we can enumerate the fragments. + //The payload is smaller than MAX_PAYLOAD_SIZE and we are able to enumerate the fragments (due to variable sizes). // --> We cann transmit the message. + // First, stop listening so we can talk. + radio.stopListening(); + + //Normal Write (Un-Fragmented) + //The len of the payload if less or equal than the max_frame_payload_size, + //therefore we send this payload in a single frame. Fragmentation not needed. + if (len <= max_frame_payload_size) { + txSuccess = _write(header,message,len,writeDirect); + radio.startListening(); + return txSuccess; + } + + noListen = 1; + + //Fragmented Write + //The len payload is definitely bigger than the max_frame_payload_size, + //therefore a fragmentation IS neded. + //Divide the message payload into chuncks of max_frame_payload_size uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) uint8_t msgCount = 0; IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); - //Iterate over the payload chuncks // Assemble a new message, copy and fill out the header // Try to send this message // If it fails // then break // return result as false - - radio.stopListening(); - - - //Normal Write (Un-Fragmented) - if(len <= max_frame_payload_size){ - txSuccess = _write(header,message,len,writeDirect); - radio.startListening(); - //printf("NRML write - type %d\n",header.type); - return txSuccess; - } - - noListen = 1; - - //Fragmented Write while (fragment_id > 0) { - - //printf("fragWrite"); + //Copy and fill out the header RF24NetworkHeader fragmentHeader = header; fragmentHeader.fragment_id = fragment_id; - //if(!isFragmented){ if (fragment_id == 1) { fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment - fragmentHeader.fragment_id = header.type; + fragmentHeader.fragment_id = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. } else { - if(msgCount == 0){ - fragmentHeader.type = NETWORK_FIRST_FRAGMENT; - }else{ - fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame - } + if (msgCount == 0) { + fragmentHeader.type = NETWORK_FIRST_FRAGMENT; + }else{ + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } } - //} + size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = std::min(len-offset,max_frame_payload_size); @@ -501,236 +475,215 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Try to send the payload chunk with the copied header bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); - if(!noListen){ - delayMicroseconds(50); - } + + if (!noListen) { + //Hack to allow the radio to be able to process the tx bytes + delayMicroseconds(50); + } + if (!ok) { - //ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); - //if(!ok){ - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); - txSuccess = false; - break; - //} + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + txSuccess = false; + break; } - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); - //Message was successful sent + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + //Check and modify counters fragment_id--; msgCount++; } - + noListen = 0; - //txSuccess = radio.txStandBy(txTimeout); - + + // Now, continue listening radio.startListening(); - - int frag_delay = uint8_t(len/48); - //if(!txSuccess){ - delay( std::min(frag_delay,20)); - //} + + int frag_delay = uint8_t(len/48); + delay( std::min(frag_delay,20)); + //Return true if all the chuncks where sent successfuly //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return txSuccess; } + /******************************************************************/ -bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect) -{ +bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect) { + // Fill out the header header.from_node = node_address; // Build the full frame to send memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); frame_size = sizeof(RF24NetworkHeader); //Set the current frame size + if (len) { memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); frame_size += len; //Set the current frame size } IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); - if (frame_size) - { - // IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); + + if (frame_size) { IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG frame size %i\n",millis(),frame_size);); IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG frame ",millis()); const char* charPtr = reinterpret_cast(frame_buffer); for (size_t i = 0; i < frame_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); } - // If the user is trying to send it to himself - if ( header.to_node == node_address ){ + if ( header.to_node == node_address ) { // Build the frame to send RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); // Just queue it in the received queue return enqueue(frame); - }else{ - if(writeDirect != 070){ - if(header.to_node == writeDirect){ + + } else { + + //Transmit to the specified destination + if (writeDirect != 070) { + if (header.to_node == writeDirect) { return write(writeDirect,2); - }else{ + } else { return write(writeDirect,3); } - }else{ + } else { // Otherwise send it out over the air return write(header.to_node,0); } } + } /******************************************************************/ -bool RF24Network::write(uint16_t to_node, uint8_t directTo) -{ - bool ok = false; +bool RF24Network::write(uint16_t to_node, uint8_t directTo) { + bool multicast = 0; // Radio ACK requested = 0 const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); // Throw it away if it's not a valid address - if ( !is_valid_address(to_node) ) + if ( !is_valid_address(to_node) ) { return false; + } - // First, stop listening so we can talk. - //radio.stopListening(); - if(to_node != parent_node && !is_direct_child(to_node) ){ - noListen=0; + if (to_node != parent_node && !is_direct_child(to_node) ) { + noListen=0; } - + // Where do we send this? By default, to our parent uint16_t send_node = parent_node; // On which pipe uint8_t send_pipe = parent_pipe%5; - if(directTo>1){ + //Select the send_pipe based on directTo or + //if this node is a direct child or a descendant + if (directTo > 1) { send_node = to_node; multicast = 1; - if(directTo == 4){ + if (directTo == 4) { send_pipe=0; } - } + } else if ( is_direct_child(to_node) ) { // If the node is a direct child, - // If the node is a direct child, - else if ( is_direct_child(to_node) ) - { // Send directly send_node = to_node; - + // To its listening pipe send_pipe = 5; - } - // If the node is a child of a child - // talk on our child's listening pipe, - // and let the direct child relay it. - else if ( is_descendant(to_node) ) - { - + + } else if ( is_descendant(to_node) ) { + // If the node is a child of a child + // talk on our child's listening pipe, + // and let the direct child relay it. + send_node = direct_child_route_to(to_node); send_pipe = 5; } - // if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK ){ - // multicast = 1; - //} - IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); - // Put the frame on the pipe - ok = write_to_pipe( send_node, send_pipe, multicast ); - //printf("Multi %d\n",multicast); + bool ok = write_to_pipe( send_node, send_pipe, multicast ); #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if(!ok){ printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); } + if (!ok) { + printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); + } #endif - - if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ){ - frame_buffer[6] = NETWORK_ACK; - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(fromAddress,1); - #if defined (SERIAL_DEBUG_ROUTING) - printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress); - #endif - } - - if(!noListen ){ - radio.startListening(); - } - // NOT NEEDED anymore. Now all reading pipes are open to start. -#if 0 - // If we are talking on our talking pipe, it's possible that no one is listening. - // If this fails, try sending it on our parent's listening pipe. That will wake - // it up, and next time it will listen to us. - - if ( !ok && send_node == parent_node ) - ok = write_to_pipe( parent_node, 0 ); -#endif + if ( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ) { - // Now, continue listening - //radio.startListening(); - - if( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ - if(!noListen){ - radio.startListening(); - } - uint32_t reply_time = millis(); - while( update() != NETWORK_ACK){ - if(millis() - reply_time > routeTimeout){ - ok=0; - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); - #endif - break; - } - } - if(!noListen){ - radio.stopListening(); - } - //if(pOK){printf("pOK\n");} - } - if(ok == true){ - nOK++; - }else{ nFails++; + frame_buffer[6] = NETWORK_ACK; + frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; + write(fromAddress,1); + + IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress);); + } + + if (!noListen ) { + radio.startListening(); + } + + if ( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )) { + + if (!noListen) { + radio.startListening(); + } + + uint32_t reply_time = millis(); + + //Wait for NETWORK_ACK + while( update() != NETWORK_ACK) { + if (millis() - reply_time > routeTimeout) { + ok = 0; + + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe);); + break; + } + } + + if (!noListen) { + radio.stopListening(); } -return ok; + + } + + //Increment the number of correctly trasmitted frames or the number of fail transmissions + if (ok == true) { + nOK++; + } else { + nFails++; + } + + return ok; } /******************************************************************/ -uint32_t lastWriteTime = 0; -bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) -{ + + +bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { + bool ok = false; uint64_t out_pipe = pipe_address( node, pipe ); - // First, stop listening so we can talk - //while(millis() - lastWriteTime < 4); - //lastWriteTime = millis(); - - if(!noListen){ - radio.stopListening(); + if (!noListen) { + radio.stopListening(); } + // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); // Retry a few times - //ok = radio.write(frame_buffer, frame_size,multicast); - radio.writeFast(frame_buffer, frame_size,multicast); - - ok = radio.txStandBy(txTimeout); - - //if(!ok){ - //update(); - //radio.writeFast(frame_buffer, frame_size,multicast); - //ok = radio.txStandBy(0); - //} - - //ok = radio.write(frame_buffer,frame_size); + radio.writeFast(frame_buffer, frame_size, multicast); + + ok = radio.txStandBy(txTimeout); IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sent on %x %s\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed"))); @@ -739,17 +692,16 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) /******************************************************************/ -const char* RF24NetworkHeader::toString(void) const -{ +const char* RF24NetworkHeader::toString(void) const { + //Convert the network header to a C-String. static char buffer[45]; - //snprintf_P(buffer,sizeof(buffer),PSTR("id %04x from 0%o to 0%o type %c"),id,from_node,to_node,type); return buffer; } /******************************************************************/ -bool RF24Network::is_direct_child( uint16_t node ) -{ +bool RF24Network::is_direct_child( uint16_t node ) { + bool result = false; // A direct child of ours has the same low numbers as us, and only @@ -771,19 +723,19 @@ bool RF24Network::is_direct_child( uint16_t node ) /******************************************************************/ -bool RF24Network::is_descendant( uint16_t node ) -{ +bool RF24Network::is_descendant( uint16_t node ) { return ( node & node_mask ) == node_address; } /******************************************************************/ -void RF24Network::setup_address(void) -{ +void RF24Network::setup_address(void) { + // First, establish the node_mask uint16_t node_mask_check = 0xFFFF; - while ( node_address & node_mask_check ) + while ( node_address & node_mask_check ) { node_mask_check <<= 3; + } node_mask = ~ node_mask_check; @@ -796,38 +748,31 @@ void RF24Network::setup_address(void) // parent pipe is the part OUT of the mask uint16_t i = node_address; uint16_t m = parent_mask; - while (m) - { + while (m) { i >>= 3; m >>= 3; } + parent_pipe = i; - //parent_pipe=i-1; -#ifdef SERIAL_DEBUG - printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe); -#endif + IF_SERIAL_DEBUG(printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe);); } /******************************************************************/ -uint16_t RF24Network::direct_child_route_to( uint16_t node ) -{ +uint16_t RF24Network::direct_child_route_to( uint16_t node ) { // Presumes that this is in fact a child!! - uint16_t child_mask = ( node_mask << 3 ) | 0B111; return node & child_mask ; } /******************************************************************/ -uint8_t RF24Network::pipe_to_descendant( uint16_t node ) -{ +uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { uint16_t i = node; uint16_t m = node_mask; - while (m) - { + while (m) { i >>= 3; m >>= 3; } @@ -837,23 +782,23 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) /******************************************************************/ -bool is_valid_address( uint16_t node ) -{ +bool is_valid_address( uint16_t node ) { bool result = true; - while(node) - { + while(node) { uint8_t digit = node & 0B111; - #if !defined (RF24NetworkMulticast) + + #if !defined (RF24NetworkMulticast) if (digit < 1 || digit > 5) - #else + #else if (digit < 0 || digit > 5) //Allow our out of range multicast address - #endif + #endif { result = false; printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); break; } + node >>= 3; } @@ -861,51 +806,52 @@ bool is_valid_address( uint16_t node ) } /******************************************************************/ + #if defined (RF24NetworkMulticast) -void RF24Network::multicastLevel(uint8_t level){ - multicast_level = level; - radio.stopListening(); - radio.openReadingPipe(0,pipe_address(levelToAddress(level),0)); - radio.startListening(); -} + void RF24Network::multicastLevel(uint8_t level) { + multicast_level = level; + radio.stopListening(); + radio.openReadingPipe(0,pipe_address(levelToAddress(level),0)); + radio.startListening(); + } -uint16_t levelToAddress(uint8_t level){ - uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); - return levelAddr; -} + uint16_t levelToAddress(uint8_t level) { + uint16_t levelAddr = 1; + levelAddr = levelAddr << ((level-1) * 3); + return levelAddr; + } #endif /******************************************************************/ -uint64_t pipe_address( uint16_t node, uint8_t pipe ) -{ +uint64_t pipe_address( uint16_t node, uint8_t pipe ) { static uint8_t address_translation[] = { 0xc3,0x3c,0x33,0xce,0x3e,0xe3,0xec }; uint64_t result = 0xCCCCCCCCCCLL; uint8_t* out = reinterpret_cast(&result); // Translate the address to use our optimally chosen radio address bytes - uint8_t count = 1; uint16_t dec = node; + uint8_t count = 1; uint16_t dec = node; + #if defined (RF24NetworkMulticast) - if(pipe != 0 || !node){ + if (pipe != 0 || !node) { #endif - while(dec){ + while (dec) { out[count]=address_translation[(dec % 8)]; // Convert our decimal values to octal, translate them to address bytes, and set our address dec /= 8; count++; } out[0] = address_translation[pipe]; // Set last byte by pipe number + #if defined (RF24NetworkMulticast) - }else{ - while(dec){ + } else { + while(dec) { dec/=8; count++; } out[1] = address_translation[count-1]; } - #endif IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast(out+1);printf_P(PSTR("%u: NET Pipe %i on node 0%o has address %x%x\n\r"),millis(),pipe,node,*top,*out)); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index cb0eff26..293cbc36 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -42,13 +42,14 @@ * * System types can also contain sub-types, included as information, TBD * - */ - + */ + /* System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 - /*System-Sub Types (0-255)*/ - #define NETWORK_REQ_STREAM 11; + +/*System-Sub Types (0-255)*/ +#define NETWORK_REQ_STREAM 11; /* System retained types */ #define NETWORK_FIRST_FRAGMENT 148 @@ -64,14 +65,12 @@ class RF24; * * The frame put over the air consists of this header and a message */ -struct RF24NetworkHeader -{ +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 fragment_id; /**< Used to count the number of fragments of the payload. Zero (0) means no more fragments left. */ - static uint16_t next_id; /**< The message ID of the next message to be sent */ /** @@ -107,6 +106,7 @@ struct RF24NetworkHeader * @return String representation of this object */ const char* toString(void) const; + }; /** @@ -114,12 +114,13 @@ struct RF24NetworkHeader * * The frame put over the air consists of a header and a message payload */ -struct RF24NetworkFrame -{ +struct RF24NetworkFrame { + RF24NetworkHeader header; /**< Header which is sent with each message */ size_t message_size; /**< The size in bytes of the payload length */ uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air */ uint8_t total_fragments; /** frame_queue; std::map, RF24NetworkFrame> frameFragmentsCache; @@ -358,6 +359,10 @@ class RF24Network 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 */ + uint16_t lastMultiMessageID; //FIXME + bool noListen; //FIXME + uint32_t lastWriteTime; //FIXME + }; /** diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 128bd954..0d4b3362 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -1,4 +1,3 @@ - /* Copyright (C) 2011 James Coliz, Jr. @@ -10,13 +9,12 @@ #ifndef __RF24_CONFIG_H__ #define __RF24_CONFIG_H__ -//#include /********** USER CONFIG **************/ -//#define RF24NetworkMulticast -//#define SERIAL_DEBUG //Change #undef to #define for full debug -#define SERIAL_DEBUG_MINIMAL //Enable minimal debugging +//#define RF24NetworkMulticast //Use multicast capabilities +//#define SERIAL_DEBUG //Set for full debug +#define SERIAL_DEBUG_MINIMAL //Enable minimal debugging //#define SERIAL_DEBUG_ROUTING //#define SERIAL_DEBUG_FRAGMENTATION @@ -24,53 +22,59 @@ // Stuff that is normally provided by Arduino #ifndef ARDUINO -#include -#include -#include -#define _BV(x) (1<<(x)) + #include + #include + #include + #define _BV(x) (1<<(x)) #endif - #if defined (SERIAL_DEBUG) -#define IF_SERIAL_DEBUG(x) ({x;}) + #define IF_SERIAL_DEBUG(x) ({x;}) #else -#define IF_SERIAL_DEBUG(x) + #define IF_SERIAL_DEBUG(x) #endif #if defined (SERIAL_DEBUG_MINIMAL) -#define IF_SERIAL_DEBUG_MINIMAL(x) ({x;}) + #define IF_SERIAL_DEBUG_MINIMAL(x) ({x;}) #else -#define IF_SERIAL_DEBUG_MINIMAL(x) + #define IF_SERIAL_DEBUG_MINIMAL(x) #endif #if defined (SERIAL_DEBUG_FRAGMENTATION) -#define IF_SERIAL_DEBUG_FRAGMENTATION(x) ({x;}) + #define IF_SERIAL_DEBUG_FRAGMENTATION(x) ({x;}) +#else + #define IF_SERIAL_DEBUG_FRAGMENTATION(x) +#endif + +#if defined (SERIAL_DEBUG_ROUTING) + #define IF_SERIAL_DEBUG_ROUTING(x) ({x;}) #else -#define IF_SERIAL_DEBUG_FRAGMENTATION(x) + #define IF_SERIAL_DEBUG_ROUTING(x) #endif // Avoid spurious warnings #if ! defined( NATIVE ) && defined( ARDUINO ) -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) + #undef PROGMEM + #define PROGMEM __attribute__(( section(".progmem.data") )) + #undef PSTR + #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) #endif // Progmem is Arduino-specific #ifdef ARDUINO -#include -#define PRIPSTR "%S" + #include + #define PRIPSTR "%S" #else -typedef char const prog_char; -typedef uint16_t prog_uint16_t; -#define PSTR(x) (x) -#define printf_P printf -#define strlen_P strlen -#define PROGMEM -#define pgm_read_word(p) (*(p)) -#define PRIPSTR "%s" + typedef char const prog_char; + typedef uint16_t prog_uint16_t; + #define PSTR(x) (x) + #define printf_P printf + #define strlen_P strlen + #define PROGMEM + #define pgm_read_word(p) (*(p)) + #define PRIPSTR "%s" #endif #endif // __RF24_CONFIG_H__ + // vim:ai:cin:sts=2 sw=2 ft=cpp From 16fe4f153a43eb34d7d64876275420efeff5a243 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 17 Sep 2014 20:01:38 +0200 Subject: [PATCH 023/105] Set MAX_USER_DEFINED_HEADER_TYPE for better readibility. --- RPi/RF24Network/RF24Network.cpp | 2 +- RPi/RF24Network/RF24Network.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 794cf4be..1268ed95 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -225,7 +225,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { result = true; - } else if (frame.header.type < 128) { + } else if (frame.header.type <= MAX_USER_DEFINED_HEADER_TYPE) { //This is not a fragmented payload but a whole frame. IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 293cbc36..892d83e5 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -44,6 +44,10 @@ * */ +/* Header types range */ +#define MIN_USER_DEFINED_HEADER_TYPE 0 +#define MAX_USER_DEFINED_HEADER_TYPE 127 + /* System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 From b1e85902e46d69209ec3ef40cde3ab7ea1b31546 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 17 Sep 2014 20:03:07 +0200 Subject: [PATCH 024/105] Add copyright --- RPi/RF24Network/RF24Network.cpp | 1 + RPi/RF24Network/RF24Network.h | 1 + RPi/RF24Network/RF24Network_config.h | 1 + 3 files changed, 3 insertions(+) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 1268ed95..885fcf0e 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2014 TMRh20 Copyright (C) 2014 Rei This program is free software; you can redistribute it and/or diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 892d83e5..6d9473dd 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -1,5 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2014 TMRh20 Copyright (C) 2014 Rei This program is free software; you can redistribute it and/or diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 0d4b3362..a901bb66 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -1,5 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2014 TMRh20 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License From e0ec131ea3e38559c291667631dfb64d0f3f4055 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 17 Sep 2014 20:23:23 +0200 Subject: [PATCH 025/105] Remove unwanted check in appendFragmentToFrame(). - This is a double check. - Only fragments can be used with this function. - Check before if the frame contains a payload fragment! --- RPi/RF24Network/RF24Network.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 885fcf0e..611147c1 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -254,13 +254,6 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { - bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); - - if (!isFragment) { - //The received payload is not a fragment. - frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; - } else - if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { // This is the first of many fragments. From 94dfbd50cecd21306e3b2bffbc34e048117c4d8f Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 18 Sep 2014 01:51:56 -0600 Subject: [PATCH 026/105] Cleanup for Aduino code - Reduced memory buffer by 64 bytes for ATTiny - Removed unused stuff - Added struct for getting routing info - Added address of pipe, to get direct child addresses - Removed type limitation on header initialization - Fix to allow multicast level 0 (node 00) - Need to align changed with RPi --- RF24Network.cpp | 229 ++++++++++++++++++++++++------------------- RF24Network.h | 143 ++++++++++++++++++++++++--- RF24Network_config.h | 2 +- 3 files changed, 258 insertions(+), 116 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index d0ad310c..53737616 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -51,8 +51,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) // Set up the radio the way we want it to look radio.setChannel(_channel); - radio.setDataRate(RF24_1MBPS); - radio.setCRCLength(RF24_CRC_16); + //radio.setDataRate(RF24_1MBPS); + //radio.setCRCLength(RF24_CRC_16); radio.enableDynamicAck(); radio.enableDynamicPayloads(); // Use different retry periods to reduce data collisions @@ -100,13 +100,12 @@ uint8_t RF24Network::update(void) { // if there is data ready uint8_t pipe_num; + uint8_t returnVal = 0; + while ( radio.isValid() && radio.available())//&pipe_num) ) { - // Dump the payloads until we've gotten everything - - //while (radio.available()) - //{ + // Dump the payloads until we've gotten everything // Fetch the payload, and see if this was the last one. radio.read( frame_buffer, sizeof(frame_buffer) ); @@ -126,9 +125,10 @@ uint8_t RF24Network::update(void) if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. #ifdef SERIAL_DEBUG_ROUTING - //printf_P(PSTR("MAC: Network ACK Rcvd \n")); + printf_P(PSTR("MAC: Network ACK Rcvd \n")); #endif - return NETWORK_ACK; + returnVal = NETWORK_ACK; + continue; } // Add it to the buffer of frames for us enqueue(); @@ -158,8 +158,8 @@ uint8_t RF24Network::update(void) write(header.to_node,1); //Send it on, indicate it is a routed payload } #else - //if(radio.available()){printf("------FLUSHED DATA --------------");} - write(header.to_node,1); //Send it on, indicate it is a routed payload + //if(radio.available()){printf("------FLUSHED DATA --------------");} + write(header.to_node,1); //Send it on, indicate it is a routed payload #endif } @@ -181,8 +181,7 @@ uint8_t RF24Network::update(void) #endif //} } - - return 0; + return returnVal; } /******************************************************************/ @@ -286,7 +285,7 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); } - return write(levelToAddress(level),4); + return write(levelToAddress(level),USER_TX_MULTICAST); } #endif @@ -324,18 +323,18 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l if ( header.to_node == node_address ){ // Just queue it in the received queue return enqueue(); - }else{ + } // Otherwise send it out over the air if(writeDirect != 070){ if(header.to_node == writeDirect){ - return write(writeDirect,2); + return write(writeDirect,USER_TX_TO_PHYSICAL_ADDRESS); }else{ - return write(writeDirect,3); + return write(writeDirect,USER_TX_TO_LOGICAL_ADDRESS); } }else{ - return write(header.to_node,0); + return write(header.to_node,TX_NORMAL); } - } + } /******************************************************************/ @@ -344,107 +343,60 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F { bool ok = false; bool multicast = 0; // Radio ACK requested = 0 - const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); - const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); + //const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) - return false; - - - // Where do we send this? By default, to our parent - uint16_t send_node = parent_node; - - // On which pipe - uint8_t send_pipe = parent_pipe %5; + return false; - - if(directTo>1){ - send_node = to_node; - multicast = 1; - if(directTo == 4){ - send_pipe=0; - } - } - - // If the node is a direct child, - else - if ( is_direct_child(to_node) ) - { - // Send directly - send_node = to_node; - // To its listening pipe - send_pipe = 5; - } - // If the node is a child of a child - // talk on our child's listening pipe, - // and let the direct child relay it. - else if ( is_descendant(to_node) ) - { - send_node = direct_child_route_to(to_node); - send_pipe = 5; - } - - - //if( ( send_node != to_node) || frame_buffer[6] == NETWORK_ACK ){ -// multicast = 0; - //} - // printf("Multi %d\n\r",multicast); + //Load info into our conversion structure, and get the converted address info + conversion.send_node = to_node; + conversion.send_pipe = directTo; + conversion.multicast = multicast; + logicalToPhysicalAddress(&conversion); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe)); - + /**Write it*/ + ok=write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); - - ok=write_to_pipe(send_node, send_pipe, multicast); - #ifdef SERIAL_DEBUG_ROUTING - if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress, send_node,send_pipe); } + if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe); } #endif - if( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address){ - frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK - //frame_buffer[7] = 0; - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; // Change the 'to' address to the 'from' address - dynLen=8; - write(fromAddress,1); // Send it back as a routed message + if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ){ + + RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); + header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK + header.to_node = header.from_node; // Change the 'to' address to the 'from' address + dynLen=8; + + conversion.send_node=header.from_node; + conversion.send_pipe = TX_ROUTED; + logicalToPhysicalAddress(&conversion); + + //Write the data using the resulting physical address + write_to_pipe(conversion.send_node, conversion.send_pipe, multicast); + dynLen=0; #if defined (SERIAL_DEBUG_ROUTING) - //if(!test){ - //printf_P(PSTR("MAC: Route OK to 0%o ACK sent to 0%o\n"),send_node,fromAddress); - //}else{ - //printf("ACK send fail"); - //} - + printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),conversion.send_node,fromAddress); #endif - } - - + } + // if(!ok){ printf_P(PSTR("%lu: MAC No Ack from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } - - //printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); - // NOT NEEDED anymore. Now all reading pipes are open to start. -#if 0 - // If we are talking on our talking pipe, it's possible that no one is listening. - // If this fails, try sending it on our parent's listening pipe. That will wake - // it up, and next time it will listen to us. - - //if ( !ok && send_node == parent_node ) - // ok = write_to_pipe( parent_node, 0 ); -#endif - #if !defined (DUAL_HEAD_RADIO) // Now, continue listening radio.startListening(); #endif - if( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )){ + if( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%lu: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe); + printf_P(PSTR("%lu: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe); #endif ok=0; break; @@ -462,6 +414,56 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F /******************************************************************/ + // Provided the to_node and directTo option, it will return the resulting node and pipe +bool RF24Network::logicalToPhysicalAddress(logicalToPhysicalStruct *conversionInfo){ + + //Create pointers so this makes sense.. kind of + //We take in the to_node(logical) now, at the end of the function, output the send_node(physical) address, etc. + //back to the original memory address that held the logical information. + uint16_t *to_node = &conversionInfo->send_node; + uint8_t *directTo = &conversionInfo->send_pipe; + bool *multicast = &conversionInfo->multicast; + + // Where do we send this? By default, to our parent + uint16_t pre_conversion_send_node = parent_node; + + // On which pipe + uint8_t pre_conversion_send_pipe = parent_pipe %5; + + if(*directTo > TX_ROUTED ){ + pre_conversion_send_node = *to_node; + *multicast = 1; + if(*directTo == USER_TX_MULTICAST){ + pre_conversion_send_pipe=0; + } + } + // If the node is a direct child, + else + if ( is_direct_child(*to_node) ) + { + // Send directly + pre_conversion_send_node = *to_node; + // To its listening pipe + pre_conversion_send_pipe = 5; + } + // If the node is a child of a child + // talk on our child's listening pipe, + // and let the direct child relay it. + else if ( is_descendant(*to_node) ) + { + pre_conversion_send_node = direct_child_route_to(*to_node); + pre_conversion_send_pipe = 5; + } + + *to_node = pre_conversion_send_node; + *directTo = pre_conversion_send_pipe; + + return 1; + +} + +/********************************************************/ + bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { @@ -478,7 +480,8 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) size_t wLen = dynLen ? dynLen: frame_size; radio.writeFast(frame_buffer, wLen,multicast); ok = radio.txStandBy(txTimeout); - + + #else radio1.openWritingPipe(out_pipe); radio1.writeFast(frame_buffer, frame_size); @@ -573,21 +576,38 @@ void RF24Network::setup_address(void) #endif } +/******************************************************************/ +uint16_t RF24Network::addressOfPipe( uint16_t node, uint8_t pipeNo ) +{ + //Say this node is 013 (1011), mask is 077 or (00111111) + //Say we want to use pipe 3 (11) + //6 bits in node mask, so shift pipeNo 6 times left and | into address + uint16_t m = node_mask >> 3; + uint8_t i=0; + + while (m){ //While there are bits left in the node mask + m>>=1; //Shift to the right + i++; //Count the # of increments + } + + return node | (pipeNo << i); +} + /******************************************************************/ uint16_t RF24Network::direct_child_route_to( uint16_t node ) { // Presumes that this is in fact a child!! - uint16_t child_mask = ( node_mask << 3 ) | 0B111; - return node & child_mask ; + return node & child_mask; + } /******************************************************************/ - +/* uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { - uint16_t i = node; + uint16_t i = node; uint16_t m = node_mask; while (m) @@ -597,7 +617,7 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) } return i & 0B111; -} +}*/ /******************************************************************/ @@ -634,8 +654,13 @@ void RF24Network::multicastLevel(uint8_t level){ } uint16_t levelToAddress(uint8_t level){ + uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); + if(level){ + levelAddr = levelAddr << ((level-1) * 3); + }else{ + return 0; + } return levelAddr; } #endif diff --git a/RF24Network.h b/RF24Network.h index 5a06ac7c..47851a5a 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -19,6 +19,36 @@ #include #include "RF24Network_config.h" +/** + * Network Management message types for management of network frames and messages + * System discard types (128 to 147) Contain no user data, just additional system sub-types sent for informational purposes. (Initially within NETWORK_ACK responses) + * System retained types (148-167) Contain user data + * + * System types can also contain sub-types, included as information, TBD + * + */ + +/** System Discard Types */ +#define NETWORK_ACK_REQUEST 128 +#define NETWORK_ACK 129 + /**System-Sub Types (0-255)*/ + #define NETWORK_REQ_STREAM 11; + +/** System retained types */ +#define NETWORK_FIRST_FRAGMENT 148 +#define NETWORK_MORE_FRAGMENTS 149 +#define NETWORK_LAST_FRAGMENT 150 +/* System retained - response messages */ +#define NETWORK_REQ_ADDRESS 151 +#define NETWORK_ADDR_RESPONSE 152 + +/** Defines for handling written payloads */ +#define TX_NORMAL 0 +#define TX_ROUTED 1 +#define USER_TX_TO_PHYSICAL_ADDRESS 2 +#define USER_TX_TO_LOGICAL_ADDRESS 3 +#define USER_TX_MULTICAST 4 + class RF24; @@ -46,10 +76,14 @@ struct RF24NetworkHeader RF24NetworkHeader() {} /** - * Send constructor - * - * Use this constructor to create a header and then send a message - * + * Send constructor + * + * @note Raspberry Pi now supports fragmentation for very long messages, send as normal. Arduino, ATTiny will handle routing of fragmented messages, but cannot receive them properly + * @warning The latest updates to add fragmentation requires updating ALL devices + * + * + * Use this constructor to create a header and then send a message + * * @code * RF24NetworkHeader header(recipient_address,'t'); * network.write(header,&message,sizeof(message)); @@ -59,8 +93,9 @@ struct RF24NetworkHeader * @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) {} - + //RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type&0x7f) {} + RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type) {} + /** * Create debugging string * @@ -73,6 +108,66 @@ struct RF24NetworkHeader const char* toString(void) const; }; + +/** + * Frame structure for each message + * + * The frame put over the air consists of a header and a message payload + */ +/** +@code + struct RF24NetworkFrame +{ + RF24NetworkHeader header; /**< Header which is sent with each message / + size_t message_size; /**< The size in bytes of the payload length / + uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air / + uint8_t total_fragments; /**New (2014): Network ACKs: Efficient acknowledgement of network-wide transmissions, via dynamic radio acks and network protocol acks. * @li New (2014): Updated addressing standard for optimal radio transmission. diff --git a/RF24Network_config.h b/RF24Network_config.h index d8b01181..4f315272 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -24,7 +24,7 @@ //#define ENABLE_SLEEP_MODE //#define RF24NetworkMulticast //#define SERIAL_DEBUG -#define SERIAL_DEBUG_ROUTING +//#define SERIAL_DEBUG_ROUTING /*************************************/ From f20337c0ca1e0b8e018c22905012a33eba0f9e04 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 18 Sep 2014 04:31:20 -0600 Subject: [PATCH 027/105] Fix compile errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RF24Network.h:357:74: sorry, unimplemented: non-static data member initializers RF24Network.h:357:74: error: ISO C++ forbids in-class initialization of non-const static member âmax_frame_payload_sizeâ RF24Network.cpp:297:9: warning: unknown conversion type character 0x20 in format [-Wformat] RF24Network.cpp:297:9: warning: unknown conversion type character 0x20 in format [-Wformat] RF24Network.cpp:297:9: warning: too many arguments for format [-Wformat-extra-args] --- RPi/RF24Network/RF24Network.cpp | 2 +- RPi/RF24Network/RF24Network.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 611147c1..e7e69101 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -294,7 +294,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { //If so, drop the assembled frame. if (frame.message_size + f->message_size > MAX_PAYLOAD_SIZE) { frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %l exceeds MAX_PAYLOAD_SIZE %l Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %d exceeds MAX_PAYLOAD_SIZE %d Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); return; } diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 6d9473dd..d1b5d14a 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -354,7 +354,7 @@ class RF24Network { RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ uint8_t frame_size; /**< How large is each frame over the air */ - size_t max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); + const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ std::queue frame_queue; std::map, RF24NetworkFrame> frameFragmentsCache; From 8261dc202c695f9a9005f5c61e958dc21a37e6da Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 18 Sep 2014 05:28:10 -0600 Subject: [PATCH 028/105] Add conversion - Added routing/address conversion separate from write function - Remove BS copyright info: This was never discussed or agreed to. This is completely free and open software. If you want to copyright things, do it elsewhere. --- RPi/RF24Network/RF24Network.cpp | 135 ++++++++++++++++++-------------- RPi/RF24Network/RF24Network.h | 29 +++++-- 2 files changed, 98 insertions(+), 66 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 611147c1..10d8d626 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -1,7 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - Copyright (C) 2014 TMRh20 - Copyright (C) 2014 Rei + Copyright (C) 2014 Rei (Initial development portion of fragmentation code only) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -294,7 +293,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { //If so, drop the assembled frame. if (frame.message_size + f->message_size > MAX_PAYLOAD_SIZE) { frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %l exceeds MAX_PAYLOAD_SIZE %l Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRM Payload of %d exceeds MAX_PAYLOAD_SIZE %d Bytes. Frame dropped\n",millis(),frame.message_size + f->message_size,MAX_PAYLOAD_SIZE);); return; } @@ -554,9 +553,8 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l bool RF24Network::write(uint16_t to_node, uint8_t directTo) { + bool ok = false; bool multicast = 0; // Radio ACK requested = 0 - const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); - const uint16_t logicalAddress = frame_buffer[2] | (frame_buffer[3] << 8); // Throw it away if it's not a valid address if ( !is_valid_address(to_node) ) { @@ -566,85 +564,54 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { if (to_node != parent_node && !is_direct_child(to_node) ) { noListen=0; } + //Load info into our conversion structure, and get the converted address info + conversion.send_node = to_node; + conversion.send_pipe = directTo; + conversion.multicast = multicast; + logicalToPhysicalAddress(&conversion); - // Where do we send this? By default, to our parent - uint16_t send_node = parent_node; - // On which pipe - uint8_t send_pipe = parent_pipe%5; - - //Select the send_pipe based on directTo or - //if this node is a direct child or a descendant - if (directTo > 1) { - send_node = to_node; - multicast = 1; - if (directTo == 4) { - send_pipe=0; - } - } else if ( is_direct_child(to_node) ) { // If the node is a direct child, - - // Send directly - send_node = to_node; - - // To its listening pipe - send_pipe = 5; - - } else if ( is_descendant(to_node) ) { - // If the node is a child of a child - // talk on our child's listening pipe, - // and let the direct child relay it. - - send_node = direct_child_route_to(to_node); - send_pipe = 5; - } - - - IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe)); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe)); // Put the frame on the pipe - bool ok = write_to_pipe( send_node, send_pipe, multicast ); + ok=write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if (!ok) { - printf_P(PSTR("%u: MAC Send fail to 0%o from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,fromAddress,send_node,send_pipe); - } + if (!ok) { printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),millis(),to_node,conversion.send_node,conversion.send_pipe); } } #endif - if ( directTo == 1 && ok && send_node == to_node && frame_buffer[6] != NETWORK_ACK && fromAddress != node_address ) { + if ( directTo == 1 && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ) { - frame_buffer[6] = NETWORK_ACK; - frame_buffer[2] = frame_buffer[0]; frame_buffer[3] = frame_buffer[1]; - write(fromAddress,1); + RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); + header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK + header.to_node = header.from_node; // Change the 'to' address to the 'from' address + + conversion.send_node=header.from_node; + conversion.send_pipe = TX_ROUTED; + logicalToPhysicalAddress(&conversion); + + //Write the data using the resulting physical address + write_to_pipe(conversion.send_node, conversion.send_pipe, multicast); - IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,fromAddress);); + IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,header.to_node);); } if (!noListen ) { radio.startListening(); } - if ( ok && (send_node != logicalAddress) && (directTo==0 || directTo == 3 )) { - - if (!noListen) { - radio.startListening(); - } + if ( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 )) { uint32_t reply_time = millis(); - //Wait for NETWORK_ACK while( update() != NETWORK_ACK) { if (millis() - reply_time > routeTimeout) { ok = 0; - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe);); break; } } - - if (!noListen) { - radio.stopListening(); - } - + if (!noListen) { radio.stopListening(); } } //Increment the number of correctly trasmitted frames or the number of fail transmissions @@ -659,7 +626,55 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { /******************************************************************/ +// Provided the to_node and directTo option, it will return the resulting node and pipe +bool RF24Network::logicalToPhysicalAddress(logicalToPhysicalStruct *conversionInfo){ + //Create pointers so this makes sense.. kind of + //We take in the to_node(logical) now, at the end of the function, output the send_node(physical) address, etc. + //back to the original memory address that held the logical information. + uint16_t *to_node = &conversionInfo->send_node; + uint8_t *directTo = &conversionInfo->send_pipe; + bool *multicast = &conversionInfo->multicast; + + // Where do we send this? By default, to our parent + uint16_t pre_conversion_send_node = parent_node; + + // On which pipe + uint8_t pre_conversion_send_pipe = parent_pipe %5; + + if(*directTo > TX_ROUTED ){ + pre_conversion_send_node = *to_node; + *multicast = 1; + if(*directTo == USER_TX_MULTICAST){ + pre_conversion_send_pipe=0; + } + } + // If the node is a direct child, + else + if ( is_direct_child(*to_node) ) + { + // Send directly + pre_conversion_send_node = *to_node; + // To its listening pipe + pre_conversion_send_pipe = 5; + } + // If the node is a child of a child + // talk on our child's listening pipe, + // and let the direct child relay it. + else if ( is_descendant(*to_node) ) + { + pre_conversion_send_node = direct_child_route_to(*to_node); + pre_conversion_send_pipe = 5; + } + + *to_node = pre_conversion_send_node; + *directTo = pre_conversion_send_pipe; + + return 1; + +} + +/******************************************************************/ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { @@ -761,7 +776,7 @@ uint16_t RF24Network::direct_child_route_to( uint16_t node ) { } /******************************************************************/ - +/* uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { uint16_t i = node; uint16_t m = node_mask; @@ -773,7 +788,7 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { return i & 0B111; } - +*/ /******************************************************************/ bool is_valid_address( uint16_t node ) { diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 6d9473dd..e072cfd1 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -1,7 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - Copyright (C) 2014 TMRh20 - Copyright (C) 2014 Rei + Copyright (C) 2014 Rei (Initial development portion of fragmentation code only) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -60,7 +59,16 @@ #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 #define NETWORK_LAST_FRAGMENT 150 +/* System retained - response messages */ +#define NETWORK_REQ_ADDRESS 151 +#define NETWORK_ADDR_RESPONSE 152 +/** Defines for handling written payloads */ +#define TX_NORMAL 0 +#define TX_ROUTED 1 +#define USER_TX_TO_PHYSICAL_ADDRESS 2 +#define USER_TX_TO_LOGICAL_ADDRESS 3 +#define USER_TX_MULTICAST 4 class RF24; @@ -330,10 +338,11 @@ class RF24Network { bool multicastRelay; #endif +uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); protected: - void open_pipes(void); - uint16_t find_node( uint16_t current_node, uint16_t target_node ); + //void open_pipes(void); + //uint16_t find_node( uint16_t current_node, uint16_t target_node ); bool write(uint16_t, uint8_t directTo); bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); bool enqueue(RF24NetworkFrame frame); @@ -341,11 +350,19 @@ class RF24Network { 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 ); + //uint8_t pipe_to_descendant( uint16_t node ); void setup_address(void); bool _write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); void appendFragmentToFrame(RF24NetworkFrame frame); + struct logicalToPhysicalStruct{ + uint16_t send_node; + uint8_t send_pipe; + bool multicast; + }conversion; + + bool logicalToPhysicalAddress(logicalToPhysicalStruct *conversionInfo); + private: #if defined (RF24NetworkMulticast) uint16_t lastMultiMessageID; @@ -354,7 +371,7 @@ class RF24Network { RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ uint8_t frame_size; /**< How large is each frame over the air */ - size_t max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); + const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ std::queue frame_queue; std::map, RF24NetworkFrame> frameFragmentsCache; From bf287d6b82adff7c72f1343fc5b24782f3c293ff Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 19 Sep 2014 01:29:02 -0600 Subject: [PATCH 029/105] fix compile errors --- RPi/RF24Network/RF24Network.cpp | 4 ++-- RPi/RF24Network/RF24Network.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 10d8d626..2c11ab7b 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -33,7 +33,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE), lastMultiMessageID(0), noListen(1), lastWriteTime(0) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE) {} /******************************************************************/ @@ -371,7 +371,7 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ header.from_node = node_address; // Build the full frame to send - RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); + //RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) { diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index e072cfd1..8c0b1aaa 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -381,7 +381,6 @@ uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); 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 */ - uint16_t lastMultiMessageID; //FIXME bool noListen; //FIXME uint32_t lastWriteTime; //FIXME From c4ddcfa79fdcac5c549ff57c231cd6dd34392311 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 25 Sep 2014 04:23:27 -0600 Subject: [PATCH 030/105] Sending large/frag payloads on Arduino - Add sending of fragmented payloads on Arduino - Currently only RPi can receive large payloads --- RF24Network.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++-- RF24Network.h | 33 +++++++++------ RF24Network_config.h | 7 ++++ 3 files changed, 119 insertions(+), 16 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 53737616..6e75cd75 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -292,11 +292,100 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ - return _write(header,message,len,070); + return write(header,message,len,070); } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ - return _write(header,message,len,writeDirect); + + bool txSuccess = true; + + //Check payload size + if (len > MAX_PAYLOAD_SIZE) { + IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size %i\n\r",millis(),MAX_PAYLOAD_SIZE);); + return false; + } + + //If the message payload is too big, whe cannot generate enough fragments + //and enumerate them + if (len > 255*max_frame_payload_size) { + txSuccess = false; + return txSuccess; + } + + //Normal Write (Un-Fragmented) + if (len <= max_frame_payload_size) { + txSuccess = _write(header,message,len,writeDirect); + //radio.startListening(); + return txSuccess; + } + + //Divide the message payload into chuncks of max_frame_payload_size + uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) + uint8_t msgCount = 0; + + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); + + while (fragment_id > 0) { + + //Copy and fill out the header + RF24NetworkHeader fragmentHeader = header; + fragmentHeader.reserved = fragment_id; + + if (fragment_id == 1) { + fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + fragmentHeader.reserved = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. + } else { + if (msgCount == 0) { + fragmentHeader.type = NETWORK_FIRST_FRAGMENT; + }else{ + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } + } + + size_t offset = msgCount*max_frame_payload_size; + size_t fragmentLen = min(len-offset,max_frame_payload_size); + + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); + + //Try to send the payload chunk with the copied header + bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + + if(!ok){ delay(100); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + if(!ok){ delay(150); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + } + } + //if (!noListen) { + delayMicroseconds(50); + //} + + if (!ok) { + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + txSuccess = false; + break; + } + + //Message was successful sent + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + + //Check and modify counters + fragment_id--; + msgCount++; + } + + //noListen = 0; + + // Now, continue listening + //radio.startListening(); + + int frag_delay = uint8_t(len/48); + delay( min(frag_delay,20)); + + //Return true if all the chunks where sent successfully + //else return false + IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); + return txSuccess; + +// return _write(header,message,len,writeDirect); } /******************************************************************/ @@ -380,7 +469,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F dynLen=0; #if defined (SERIAL_DEBUG_ROUTING) - printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),conversion.send_node,fromAddress); + printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); #endif } diff --git a/RF24Network.h b/RF24Network.h index 47851a5a..c10d6271 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -27,7 +27,11 @@ * System types can also contain sub-types, included as information, TBD * */ - + +/* Header types range */ +#define MIN_USER_DEFINED_HEADER_TYPE 0 +#define MAX_USER_DEFINED_HEADER_TYPE 127 + /** System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 @@ -49,6 +53,8 @@ #define USER_TX_TO_LOGICAL_ADDRESS 3 #define USER_TX_MULTICAST 4 +#define MAX_FRAME_SIZE 32 +#define MAX_PAYLOAD_SIZE 1500 class RF24; @@ -114,26 +120,26 @@ struct RF24NetworkHeader * * The frame put over the air consists of a header and a message payload */ -/** -@code + + struct RF24NetworkFrame { - RF24NetworkHeader header; /**< Header which is sent with each message / - size_t message_size; /**< The size in bytes of the payload length / - uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air / - uint8_t total_fragments; /** Date: Thu, 25 Sep 2014 04:44:43 -0600 Subject: [PATCH 031/105] define disable_fragmentation for Arduino - Add user define to disable sending of large/fragmented payloads on Arduino (saves program space) --- RF24Network.cpp | 5 ++++- RF24Network_config.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 6e75cd75..b5bb9ed0 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -297,6 +297,9 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ +#if defined (DISABLE_FRAGMENTATION) + return _write(header,message,len,writeDirect); +#else bool txSuccess = true; //Check payload size @@ -385,7 +388,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return txSuccess; -// return _write(header,message,len,writeDirect); +#endif //Fragmentation enabled } /******************************************************************/ diff --git a/RF24Network_config.h b/RF24Network_config.h index 7f934739..2d22ea30 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -23,6 +23,8 @@ //#define DUAL_HEAD_RADIO //#define ENABLE_SLEEP_MODE //#define RF24NetworkMulticast +//#define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation + //#define SERIAL_DEBUG //#define SERIAL_DEBUG_ROUTING //#define SERIAL_DEBUG_FRAGMENTATION From 48de4994494007a9d9faa832e8b7b4f98c407092 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 27 Sep 2014 01:24:24 -0600 Subject: [PATCH 032/105] Testing some changes/additions - Add NETWORK_POLL for finding responsive nodes - Add fragmented writes for AVR/Due etc. - Test support for address request types --- RF24Network.cpp | 134 +++++++++++++++++++++++++++++++++++++++++-- RF24Network.h | 39 +++++++------ RF24Network_config.h | 11 +++- 3 files changed, 160 insertions(+), 24 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 53737616..fbc74641 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -48,7 +48,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) if ( ! radio.isValid() ){ return; } - + + radio.stopListening(); // Set up the radio the way we want it to look radio.setChannel(_channel); //radio.setDataRate(RF24_1MBPS); @@ -110,7 +111,7 @@ uint8_t RF24Network::update(void) radio.read( frame_buffer, sizeof(frame_buffer) ); // Read the beginning of the frame as the header - const RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); + RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Received on %u %s\n\r"),millis(),pipe_num,header.toString())); IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); @@ -130,7 +131,27 @@ uint8_t RF24Network::update(void) returnVal = NETWORK_ACK; continue; } // Add it to the buffer of frames for us - + if(header.type == NETWORK_ADDR_RESPONSE ){ + uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; + requester |= frame_buffer[9] << 8; + + if(requester != node_address){ + header.to_node = requester; + //header.from_node = node_address; + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //printf("fwd addr resp to 0%o , this node: 0%o\n", requester,node_address); + continue; + } + } + if(header.type == NETWORK_REQ_ADDRESS && node_address){ + //RF24NetworkHeader reqHeader = header; + header.from_node = node_address; + header.to_node = 0; + write(header.to_node,TX_NORMAL); + //printf("fwd addr req\n"); + continue; + } enqueue(); @@ -146,6 +167,17 @@ uint8_t RF24Network::update(void) #endif write(levelToAddress(multicast_level)<<3,4); } + + if(header.type == NETWORK_POLL ){ + header.to_node = header.from_node; + header.from_node = node_address; + delay(2); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //Serial.println("send poll"); + continue; + } + enqueue(); lastMultiMessageID = header.id; } @@ -292,11 +324,103 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len){ - return _write(header,message,len,070); + return write(header,message,len,070); } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ + +#if defined (DISABLE_FRAGMENTATION) return _write(header,message,len,writeDirect); +#else + bool txSuccess = true; + + //Check payload size + if (len > MAX_PAYLOAD_SIZE) { + IF_SERIAL_DEBUG(printf("%u: NET write message failed. Given 'len' is bigger than the MAX Payload size %i\n\r",millis(),MAX_PAYLOAD_SIZE);); + return false; + } + + //If the message payload is too big, whe cannot generate enough fragments + //and enumerate them + if (len > 255*max_frame_payload_size) { + txSuccess = false; + return txSuccess; + } + + //Normal Write (Un-Fragmented) + if (len <= max_frame_payload_size) { + txSuccess = _write(header,message,len,writeDirect); + //radio.startListening(); + return txSuccess; + } + + //Divide the message payload into chuncks of max_frame_payload_size + uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) + uint8_t msgCount = 0; + + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); + + while (fragment_id > 0) { + + //Copy and fill out the header + RF24NetworkHeader fragmentHeader = header; + fragmentHeader.reserved = fragment_id; + + if (fragment_id == 1) { + fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + fragmentHeader.reserved = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. + } else { + if (msgCount == 0) { + fragmentHeader.type = NETWORK_FIRST_FRAGMENT; + }else{ + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + } + } + + size_t offset = msgCount*max_frame_payload_size; + size_t fragmentLen = min(len-offset,max_frame_payload_size); + + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); + + //Try to send the payload chunk with the copied header + bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + + if(!ok){ delay(100); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + if(!ok){ delay(150); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + } + } + //if (!noListen) { + delayMicroseconds(50); + //} + + if (!ok) { + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + txSuccess = false; + break; + } + + //Message was successful sent + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + + //Check and modify counters + fragment_id--; + msgCount++; + } + + //noListen = 0; + + // Now, continue listening + //radio.startListening(); + + int frag_delay = uint8_t(len/48); + delay( min(frag_delay,20)); + + //Return true if all the chunks where sent successfully + //else return false + IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); + return txSuccess; + +#endif //Fragmentation enabled } /******************************************************************/ @@ -380,7 +504,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F dynLen=0; #if defined (SERIAL_DEBUG_ROUTING) - printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),conversion.send_node,fromAddress); + printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); #endif } diff --git a/RF24Network.h b/RF24Network.h index 47851a5a..d18f9e8f 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -27,12 +27,17 @@ * System types can also contain sub-types, included as information, TBD * */ - + +/* Header types range */ +#define MIN_USER_DEFINED_HEADER_TYPE 0 +#define MAX_USER_DEFINED_HEADER_TYPE 127 + /** System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 /**System-Sub Types (0-255)*/ #define NETWORK_REQ_STREAM 11; +#define NETWORK_POLL 130 /** System retained types */ #define NETWORK_FIRST_FRAGMENT 148 @@ -49,6 +54,8 @@ #define USER_TX_TO_LOGICAL_ADDRESS 3 #define USER_TX_MULTICAST 4 +#define MAX_FRAME_SIZE 32 +#define MAX_PAYLOAD_SIZE 1500 class RF24; @@ -114,26 +121,26 @@ struct RF24NetworkHeader * * The frame put over the air consists of a header and a message payload */ -/** -@code + + struct RF24NetworkFrame { - RF24NetworkHeader header; /**< Header which is sent with each message / - size_t message_size; /**< The size in bytes of the payload length / - uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air / - uint8_t total_fragments; /** Date: Sat, 27 Sep 2014 01:31:34 -0600 Subject: [PATCH 033/105] Testing some RPi changes/additions - Modify fragmented payload writing - Add network_poll and address request handling --- RPi/RF24Network/RF24Network.cpp | 119 ++++++++++++++++++++------- RPi/RF24Network/RF24Network.h | 3 +- RPi/RF24Network/RF24Network_config.h | 2 +- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 10d8d626..4fa6ade3 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -33,7 +33,7 @@ uint32_t nFails = 0, nOK=0; /******************************************************************/ -RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE), lastMultiMessageID(0), noListen(1), lastWriteTime(0) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), frame_size(MAX_FRAME_SIZE) {} /******************************************************************/ @@ -48,7 +48,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { if ( ! radio.isValid() ) { return; } - + radio.stopListening(); // Set up the radio the way we want it to look radio.setChannel(_channel); radio.setDataRate(RF24_1MBPS); @@ -138,6 +138,28 @@ uint8_t RF24Network::update(void) { returnVal = NETWORK_ACK; continue; } + if(header.type == NETWORK_ADDR_RESPONSE ){ + uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; + requester |= frame_buffer[9] << 8; + + if(requester != node_address){ + header.to_node = requester; + //header.from_node = node_address; + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //printf("fwd addr resp to 0%o , this node: 0%o\n", requester,node_address); + continue; + } + } + if(header.type == NETWORK_REQ_ADDRESS && node_address){ + //RF24NetworkHeader reqHeader = header; + header.from_node = node_address; + header.to_node = 0; + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + write(header.to_node,TX_NORMAL); + //printf("fwd addr req\n"); + continue; + } enqueue(frame); @@ -155,10 +177,18 @@ uint8_t RF24Network::update(void) { IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1);); write(levelToAddress(multicast_level)<<3,4); } - + if(header.type == NETWORK_POLL ){ + header.to_node = header.from_node; + header.from_node = node_address; + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + delay(2); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //printf("send poll\n"); + continue; + } + lastMultiMessageID = header.id; enqueue(frame); - } else { //Duplicate received IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: Drop duplicate multicast frame %u from 0%o\n"),header.id,header.from_node);); @@ -225,7 +255,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { result = true; - } else if (frame.header.type <= MAX_USER_DEFINED_HEADER_TYPE) { + } else{// if (frame.header.type <= MAX_USER_DEFINED_HEADER_TYPE) { //This is not a fragmented payload but a whole frame. IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue @%x "),millis(),frame_queue.size())); @@ -233,12 +263,12 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { frame_queue.push(frame); result = true; - } else { + }/* else { //Undefined/Unknown header.type received. Drop frame! IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.fragment_id); ); //The frame is not explicitly dropped, but the given object is ignored. //FIXME: does this causes problems with memory management? - } + }*/ if (result) { IF_SERIAL_DEBUG(printf("ok\n\r")); @@ -253,6 +283,8 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { + + if(frameFragmentsCache.size() >= 7){ frameFragmentsCache.erase(frameFragmentsCache.begin()); } if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { // This is the first of many fragments. @@ -281,7 +313,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { if (frame.header.fragment_id != f->header.fragment_id) { if (frame.header.fragment_id > f->header.fragment_id) { IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); - frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); + //frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); } else { frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); @@ -371,7 +403,7 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ header.from_node = node_address; // Build the full frame to send - RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); + //RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); if (len) { @@ -415,18 +447,18 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // --> We cann transmit the message. // First, stop listening so we can talk. - radio.stopListening(); + //radio.stopListening(); //Normal Write (Un-Fragmented) //The len of the payload if less or equal than the max_frame_payload_size, //therefore we send this payload in a single frame. Fragmentation not needed. if (len <= max_frame_payload_size) { txSuccess = _write(header,message,len,writeDirect); - radio.startListening(); + //radio.startListening(); return txSuccess; } - noListen = 1; + //noListen = 1; //Fragmented Write //The len payload is definitely bigger than the max_frame_payload_size, @@ -466,13 +498,18 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); + //uint8_t msg[32]; + //Try to send the payload chunk with the copied header - bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); + bool ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - if (!noListen) { - //Hack to allow the radio to be able to process the tx bytes + if(!ok){ delay(100); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + if(!ok){ delay(150); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + } + } + //if (!noListen) { delayMicroseconds(50); - } + //} if (!ok) { IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); @@ -488,10 +525,10 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le msgCount++; } - noListen = 0; + //noListen = 0; // Now, continue listening - radio.startListening(); + //radio.startListening(); int frag_delay = uint8_t(len/48); delay( std::min(frag_delay,20)); @@ -561,9 +598,9 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { return false; } - if (to_node != parent_node && !is_direct_child(to_node) ) { - noListen=0; - } + //if (to_node != parent_node && !is_direct_child(to_node) ) { + // noListen=0; + //} //Load info into our conversion structure, and get the converted address info conversion.send_node = to_node; conversion.send_pipe = directTo; @@ -582,11 +619,13 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { if ( directTo == 1 && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ) { - RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); - header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK - header.to_node = header.from_node; // Change the 'to' address to the 'from' address - - conversion.send_node=header.from_node; + //RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); + frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK + //header.to_node = header.from_node; // Change the 'to' address to the 'from' address + frame_buffer[2] = frame_buffer[0]; + frame_buffer[3] = frame_buffer[1]; + + conversion.send_node = (frame_buffer[0] | frame_buffer[1] << 8);//header.from_node; conversion.send_pipe = TX_ROUTED; logicalToPhysicalAddress(&conversion); @@ -596,9 +635,9 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,header.to_node);); } - if (!noListen ) { + //if (!noListen ) { radio.startListening(); - } + //} if ( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 )) { @@ -611,7 +650,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { break; } } - if (!noListen) { radio.stopListening(); } + //if (!noListen) { radio.stopListening(); } } //Increment the number of correctly trasmitted frames or the number of fail transmissions @@ -682,9 +721,9 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { uint64_t out_pipe = pipe_address( node, pipe ); - if (!noListen) { + //if (!noListen) { radio.stopListening(); - } + //} // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); @@ -769,6 +808,24 @@ void RF24Network::setup_address(void) { /******************************************************************/ +uint16_t RF24Network::addressOfPipe( uint16_t node, uint8_t pipeNo ) +{ + //Say this node is 013 (1011), mask is 077 or (00111111) + //Say we want to use pipe 3 (11) + //6 bits in node mask, so shift pipeNo 6 times left and | into address + uint16_t m = node_mask >> 3; + uint8_t i=0; + + while (m){ //While there are bits left in the node mask + m>>=1; //Shift to the right + i++; //Count the # of increments + } + + return node | (pipeNo << i); +} + +/******************************************************************/ + uint16_t RF24Network::direct_child_route_to( uint16_t node ) { // Presumes that this is in fact a child!! uint16_t child_mask = ( node_mask << 3 ) | 0B111; diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index e072cfd1..ab34988c 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -51,6 +51,7 @@ /* System Discard Types */ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 +#define NETWORK_POLL 130 /*System-Sub Types (0-255)*/ #define NETWORK_REQ_STREAM 11; @@ -376,12 +377,10 @@ uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); std::queue frame_queue; std::map, RF24NetworkFrame> frameFragmentsCache; - 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 */ - uint16_t lastMultiMessageID; //FIXME bool noListen; //FIXME uint32_t lastWriteTime; //FIXME diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index a901bb66..1930a27f 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -13,7 +13,7 @@ /********** USER CONFIG **************/ -//#define RF24NetworkMulticast //Use multicast capabilities +#define RF24NetworkMulticast //Use multicast capabilities //#define SERIAL_DEBUG //Set for full debug #define SERIAL_DEBUG_MINIMAL //Enable minimal debugging //#define SERIAL_DEBUG_ROUTING From 5f29c228b2bbf273bce6061c3129bf4359db6e22 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 27 Sep 2014 13:24:13 -0600 Subject: [PATCH 034/105] Small fixes - Fix multicast - Fix debug - Fix/Enable multicast level 0 - Enable user defined types > 127 --- RPi/RF24Network/RF24Network.cpp | 42 ++++++++++++++++----------------- RPi/RF24Network/RF24Network.h | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 4fa6ade3..d43473fc 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -89,13 +89,11 @@ void RF24Network::failures(uint32_t *_fails, uint32_t *_ok) { uint8_t RF24Network::update(void) { // if there is data ready uint8_t returnVal = 0; + uint8_t pipe_num; -#if defined (SERIAL_DEBUG) while ( radio.isValid() && radio.available(&pipe_num) ) -#else - while ( radio.isValid() && radio.available() ) -#endif { + // Fetch the payload, and get the size size_t len = radio.getDynamicPayloadSize(); if (len == 0) { @@ -186,7 +184,6 @@ uint8_t RF24Network::update(void) { //printf("send poll\n"); continue; } - lastMultiMessageID = header.id; enqueue(frame); } else { @@ -398,23 +395,22 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level) { - // Fill out the header header.to_node = 0100; header.from_node = node_address; // Build the full frame to send - //RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(sizeof(message),len)); - - IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Sending %s\n\r"),millis(),header.toString())); - if (len) { - IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%u: NET message %04x\n\r"),millis(),*i)); - } - - uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); - - return write(levelAddr,4); + memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + if (len) + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); + IF_SERIAL_DEBUG(printf("%u: NET Sending %s\n\r",millis(),header.toString());); + if (len) + { + IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf("%u: NET message %04x\n\r",millis(),*i);); + } + + return write(levelToAddress(level),USER_TX_MULTICAST); + } #endif //RF24NetworkMulticast @@ -613,7 +609,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { ok=write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); #if defined (SERIAL_DEBUG_ROUTING) || defined(SERIAL_DEBUG) - if (!ok) { printf_P(PSTR("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),millis(),to_node,conversion.send_node,conversion.send_pipe); } } + if (!ok) { printf("%u: MAC Send fail to 0%o via 0%o on pipe %x\n\r",millis(),to_node,conversion.send_node,conversion.send_pipe); } #endif @@ -882,9 +878,13 @@ bool is_valid_address( uint16_t node ) { } uint16_t levelToAddress(uint8_t level) { - uint16_t levelAddr = 1; - levelAddr = levelAddr << ((level-1) * 3); - return levelAddr; + uint16_t levelAddr = 1; + if(level){ + levelAddr = levelAddr << ((level-1) * 3); + }else{ + return 0; + } + return levelAddr; } #endif diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index ab34988c..0f6f22a2 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -108,7 +108,7 @@ struct RF24NetworkHeader { * @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) {} + RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type) {} /** * Create debugging string From 420ca1ed8bc97ce27fbe473fb11529f141cba140 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 28 Sep 2014 07:04:58 -0600 Subject: [PATCH 035/105] Minor updates for RF24Mesh - Update config file - Revert header.fragment_id to header.reserved to realign with Arduino - Add is_valid_address to public functions --- RF24Network.cpp | 36 ++++++++++++------- RF24Network.h | 3 +- RF24Network_config.h | 5 --- RPi/RF24Network/RF24Network.cpp | 54 ++++++++++++++++------------ RPi/RF24Network/RF24Network.h | 10 ++++-- RPi/RF24Network/RF24Network_config.h | 8 +++-- 6 files changed, 71 insertions(+), 45 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index fbc74641..13c345a3 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -80,12 +80,12 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) radio.openReadingPipe(i,pipe_address(_node_address,i)); } #if defined (RF24NetworkMulticast) - uint8_t count = 0; - while(_node_address){ - _node_address/=8; - count++; - } - multicast_level = count; + uint8_t count = 0; + while(_node_address) { + _node_address/=8; + count++; + } + multicast_level = count; #endif radio.startListening(); @@ -140,7 +140,7 @@ uint8_t RF24Network::update(void) //header.from_node = node_address; write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //printf("fwd addr resp to 0%o , this node: 0%o\n", requester,node_address); + //printf("fwd addr resp to 0%o , this node: 0%o \n", requester,node_address); continue; } } @@ -152,9 +152,20 @@ uint8_t RF24Network::update(void) //printf("fwd addr req\n"); continue; } - enqueue(); - - + + // This ensures that all address requests are only placed at the front of the queue + if( header.type == NETWORK_REQ_ADDRESS ){ + + if(available()){ + //printf("Dropped address request\n"); + return returnVal; + }else{ + enqueue(); + return returnVal; + } + } + enqueue(); + }else{ dynLen = radio.getDynamicPayloadSize(); if(!dynLen){continue;} @@ -171,7 +182,8 @@ uint8_t RF24Network::update(void) if(header.type == NETWORK_POLL ){ header.to_node = header.from_node; header.from_node = node_address; - delay(2); + + delay(node_address%5); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //Serial.println("send poll"); @@ -745,7 +757,7 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) /******************************************************************/ -bool is_valid_address( uint16_t node ) +bool RF24Network::is_valid_address( uint16_t node ) { bool result = true; diff --git a/RF24Network.h b/RF24Network.h index d18f9e8f..2fdad837 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -385,7 +385,7 @@ class RF24Network #endif uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); - + bool is_valid_address( uint16_t node ); private: bool write(uint16_t, uint8_t directTo); @@ -394,6 +394,7 @@ class RF24Network 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); diff --git a/RF24Network_config.h b/RF24Network_config.h index 61093af3..8b501b32 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -22,13 +22,8 @@ //#define DUAL_HEAD_RADIO //#define ENABLE_SLEEP_MODE -<<<<<<< HEAD -//#define RF24NetworkMulticast -//#define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation -======= #define RF24NetworkMulticast #define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation ->>>>>>> origin/TMRh20 //#define SERIAL_DEBUG //#define SERIAL_DEBUG_ROUTING diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index d43473fc..d2b9b3ee 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -51,8 +51,8 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { radio.stopListening(); // Set up the radio the way we want it to look radio.setChannel(_channel); - radio.setDataRate(RF24_1MBPS); - radio.setCRCLength(RF24_CRC_16); + //radio.setDataRate(RF24_1MBPS); + //radio.setCRCLength(RF24_CRC_16); radio.enableDynamicAck(); radio.enableDynamicPayloads(); @@ -76,6 +76,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { count++; } multicast_level = count; + #endif radio.startListening(); } @@ -158,7 +159,16 @@ uint8_t RF24Network::update(void) { //printf("fwd addr req\n"); continue; } - + // This ensures that all address requests are only placed at the front of the queue + if( header.type == NETWORK_REQ_ADDRESS ){ + if(available()){ + printf("Dropped address request\n"); + return returnVal; + }else{ + enqueue(frame); + return returnVal; + } + } enqueue(frame); if (radio.rxFifoFull()) { @@ -179,7 +189,7 @@ uint8_t RF24Network::update(void) { header.to_node = header.from_node; header.from_node = node_address; memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); - delay(2); + delay(5); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //printf("send poll\n"); continue; @@ -220,11 +230,11 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { return true; } - if ((frame.header.fragment_id > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ) { + if ((frame.header.reserved > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ) { //The received frame contains the a fragmented payload //Set the more fragments flag to indicate a fragmented frame - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG fragmented payload of size %i Bytes with fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.reserved);); //Append payload appendFragmentToFrame(frame); @@ -232,9 +242,9 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { } else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { //The last fragment flag indicates that we received the last fragment - //Workaround/Hack: In this case the header.fragment_id does not count the number of fragments but is a copy of the type flags specified by the user application. + //Workaround/Hack: In this case the header.reserved does not count the number of fragments but is a copy of the type flags specified by the user application. - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.reserved);); //Append payload appendFragmentToFrame(frame); @@ -242,7 +252,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { //Push the assembled frame in the frame_queue and remove it from cache // If total_fragments == 0, we are finished - if ( frame.header.to_node == node_address || (!frame.header.fragment_id && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ) { + if ( frame.header.to_node == node_address || (!frame.header.reserved && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ) { frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); } else { IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); @@ -262,7 +272,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { }/* else { //Undefined/Unknown header.type received. Drop frame! - IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.fragment_id); ); + IF_SERIAL_DEBUG_MINIMAL( printf("%u: FRG Received unknown or system header type %d with fragment id %d\n",millis(),frame.header.type, frame.header.reserved); ); //The frame is not explicitly dropped, but the given object is ignored. //FIXME: does this causes problems with memory management? }*/ @@ -287,7 +297,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { if (frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)) { // Decrement the stored total_fragments counter - frame.header.fragment_id = frame.header.fragment_id-1; + frame.header.reserved = frame.header.reserved-1; // Cache the fragment frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] = frame; } else { @@ -301,19 +311,19 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); if (frame.header.type == NETWORK_LAST_FRAGMENT) { - //Workaround/Hack: The user application specified header.type is sent with the last fragment in the fragment_id field - frame.header.type = frame.header.fragment_id; - frame.header.fragment_id = 1; + //Workaround/Hack: The user application specified header.type is sent with the last fragment in the reserved field + frame.header.type = frame.header.reserved; + frame.header.reserved = 1; } //Error checking for missed fragments and payload size - if (frame.header.fragment_id != f->header.fragment_id) { - if (frame.header.fragment_id > f->header.fragment_id) { - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + if (frame.header.reserved != f->header.reserved) { + if (frame.header.reserved > f->header.reserved) { + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Duplicate or out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.reserved,f->header.reserved);); //frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); } else { frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); - IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.fragment_id,f->header.fragment_id);); + IF_SERIAL_DEBUG_MINIMAL(printf("%u: FRG Out of sequence frame %d, expected %d. Cleared.\n",millis(),frame.header.reserved,f->header.reserved);); } return; } @@ -330,7 +340,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { memcpy(f->message_buffer+f->message_size, frame.message_buffer, frame.message_size); f->message_size += frame.message_size; //Increment message size f->header = frame.header; //Update header - f->header.fragment_id--; //Update Total fragments + f->header.reserved--; //Update Total fragments } } @@ -476,11 +486,11 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Copy and fill out the header RF24NetworkHeader fragmentHeader = header; - fragmentHeader.fragment_id = fragment_id; + fragmentHeader.reserved = fragment_id; if (fragment_id == 1) { fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment - fragmentHeader.fragment_id = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. + fragmentHeader.reserved = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.reserved. } else { if (msgCount == 0) { fragmentHeader.type = NETWORK_FIRST_FRAGMENT; @@ -844,7 +854,7 @@ uint8_t RF24Network::pipe_to_descendant( uint16_t node ) { */ /******************************************************************/ -bool is_valid_address( uint16_t node ) { +bool RF24Network::is_valid_address( uint16_t node ){ bool result = true; while(node) { diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 0f6f22a2..a1394f4f 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -84,7 +84,7 @@ struct RF24NetworkHeader { 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 fragment_id; /**< Used to count the number of fragments of the payload. Zero (0) means no more fragments left. */ + unsigned char reserved; /**< Used to count the number of fragments of the payload. Zero (0) means no more fragments left. */ static uint16_t next_id; /**< The message ID of the next message to be sent */ /** @@ -296,7 +296,9 @@ class RF24Network { * Methods you can use to drive the network in more advanced ways */ /**@{*/ -#if defined RF24NetworkMulticast + + +#if defined (RF24NetworkMulticast) /** * Send a multicast message to multiple nodes at once * Allows messages to be rapidly broadcast through the network @@ -337,8 +339,10 @@ class RF24Network { */ bool multicastRelay; - + #endif + +bool is_valid_address( uint16_t node ); uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); protected: diff --git a/RPi/RF24Network/RF24Network_config.h b/RPi/RF24Network/RF24Network_config.h index 1930a27f..b6aa305f 100644 --- a/RPi/RF24Network/RF24Network_config.h +++ b/RPi/RF24Network/RF24Network_config.h @@ -7,8 +7,8 @@ version 2 as published by the Free Software Foundation. */ -#ifndef __RF24_CONFIG_H__ -#define __RF24_CONFIG_H__ +#ifndef __RF24NETWORK_CONFIG_H__ +#define __RF24NETWORK_CONFIG_H__ /********** USER CONFIG **************/ @@ -21,6 +21,10 @@ /*************************************/ +#endif + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ // Stuff that is normally provided by Arduino #ifndef ARDUINO #include From eff7ee0a6f4636573274c81856f7f0f723ec0ad1 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 28 Sep 2014 17:35:02 -0600 Subject: [PATCH 036/105] Update config file --- RF24Network_config.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/RF24Network_config.h b/RF24Network_config.h index 8b501b32..0ed541da 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -7,8 +7,8 @@ version 2 as published by the Free Software Foundation. */ -#ifndef __RF24_CONFIG_H__ -#define __RF24_CONFIG_H__ +#ifndef __RF24NETWORK_CONFIG_H__ +#define __RF24NETWORK_CONFIG_H__ #if ARDUINO < 100 #include @@ -30,6 +30,10 @@ //#define SERIAL_DEBUG_FRAGMENTATION /*************************************/ +#endif + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ // Define _BV for non-Arduino platforms and for Arduino DUE #if defined (ARDUINO) && !defined (__arm__) From 324e413d942a2ecda34372da27a65c1152050e96 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Mon, 29 Sep 2014 11:41:07 -0600 Subject: [PATCH 037/105] Cleanup, change non-debug printf, minor changes Moving call to printf into the DEBUG_MINIMAL statement can reduce code size quite a bit on Arduino . Cleanup debugging a bit --- RF24Network.cpp | 53 ++++++++++++++++++-------------------------- RF24Network.h | 5 ++++- RF24Network_config.h | 13 +++++++++++ 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 13c345a3..76657d0e 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -125,9 +125,7 @@ uint8_t RF24Network::update(void) // Is this for us? if ( header.to_node == node_address ){ if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: Network ACK Rcvd \n")); - #endif + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Network ACK Rcvd \n")); ); returnVal = NETWORK_ACK; continue; } // Add it to the buffer of frames for us @@ -173,9 +171,7 @@ uint8_t RF24Network::update(void) if( header.to_node == 0100){ if(header.id != lastMultiMessageID){ if(multicastRelay){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); - #endif + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); ); write(levelToAddress(multicast_level)<<3,4); } @@ -193,11 +189,9 @@ uint8_t RF24Network::update(void) enqueue(); lastMultiMessageID = header.id; } - #ifdef SERIAL_DEBUG_ROUTING else{ - printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); - } - #endif + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); ); + } }else{ write(header.to_node,1); //Send it on, indicate it is a routed payload } @@ -311,6 +305,7 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) return bufsize; } + #if defined RF24NetworkMulticast /******************************************************************/ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level){ @@ -344,7 +339,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le #if defined (DISABLE_FRAGMENTATION) return _write(header,message,len,writeDirect); #else - bool txSuccess = true; + //bool txSuccess = true; //Check payload size if (len > MAX_PAYLOAD_SIZE) { @@ -355,15 +350,12 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //If the message payload is too big, whe cannot generate enough fragments //and enumerate them if (len > 255*max_frame_payload_size) { - txSuccess = false; - return txSuccess; + return false; } //Normal Write (Un-Fragmented) if (len <= max_frame_payload_size) { - txSuccess = _write(header,message,len,writeDirect); - //radio.startListening(); - return txSuccess; + return _write(header,message,len,writeDirect); } //Divide the message payload into chuncks of max_frame_payload_size @@ -388,7 +380,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } } - + size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = min(len-offset,max_frame_payload_size); @@ -407,7 +399,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (!ok) { IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); - txSuccess = false; + return false; break; } @@ -430,7 +422,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Return true if all the chunks where sent successfully //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); - return txSuccess; + return true; #endif //Fragmentation enabled } @@ -496,15 +488,15 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F /**Write it*/ ok=write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); - #ifdef SERIAL_DEBUG_ROUTING - if(!ok){ printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe); } - #endif + + if(!ok){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe);); } if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ){ RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK header.to_node = header.from_node; // Change the 'to' address to the 'from' address + dynLen=8; conversion.send_node=header.from_node; @@ -515,9 +507,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F write_to_pipe(conversion.send_node, conversion.send_pipe, multicast); dynLen=0; - #if defined (SERIAL_DEBUG_ROUTING) - printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); - #endif + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); ); } // if(!ok){ printf_P(PSTR("%lu: MAC No Ack from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } @@ -531,9 +521,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ - #ifdef SERIAL_DEBUG_ROUTING - printf_P(PSTR("%lu: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe); - #endif + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu: MAC Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe); ); ok=0; break; } @@ -707,9 +695,9 @@ void RF24Network::setup_address(void) parent_pipe = i; //parent_pipe = i-1; -#ifdef SERIAL_DEBUG - printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe); -#endif + + IF_SERIAL_DEBUG( printf_P(PSTR("setup_address node=0%o mask=0%o parent=0%o pipe=0%o\n\r"),node_address,node_mask,parent_node,parent_pipe);); + } /******************************************************************/ @@ -771,7 +759,8 @@ bool RF24Network::is_valid_address( uint16_t node ) #endif { result = false; - printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); + IF_SERIAL_DEBUG_MINIMAL(printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node);); + printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); break; } node >>= 3; diff --git a/RF24Network.h b/RF24Network.h index 2fdad837..296bcb53 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -36,7 +36,7 @@ #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 /**System-Sub Types (0-255)*/ - #define NETWORK_REQ_STREAM 11; + //#define NETWORK_REQ_STREAM 11; #define NETWORK_POLL 130 /** System retained types */ @@ -54,9 +54,12 @@ #define USER_TX_TO_LOGICAL_ADDRESS 3 #define USER_TX_MULTICAST 4 +/** System defines */ #define MAX_FRAME_SIZE 32 #define MAX_PAYLOAD_SIZE 1500 + + class RF24; /** diff --git a/RF24Network_config.h b/RF24Network_config.h index 0ed541da..91c9d7c0 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -26,6 +26,7 @@ #define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation //#define SERIAL_DEBUG +//#define SERIAL_DEBUG_MINIMAL //#define SERIAL_DEBUG_ROUTING //#define SERIAL_DEBUG_FRAGMENTATION /*************************************/ @@ -71,12 +72,24 @@ #define printf_P(...) #endif #endif + + #if defined (SERIAL_DEBUG_MINIMAL) + #define IF_SERIAL_DEBUG_MINIMAL(x) ({x;}) + #else + #define IF_SERIAL_DEBUG_MINIMAL(x) + #endif #if defined (SERIAL_DEBUG_FRAGMENTATION) #define IF_SERIAL_DEBUG_FRAGMENTATION(x) ({x;}) #else #define IF_SERIAL_DEBUG_FRAGMENTATION(x) #endif + + #if defined (SERIAL_DEBUG_ROUTING) + #define IF_SERIAL_DEBUG_ROUTING(x) ({x;}) + #else + #define IF_SERIAL_DEBUG_ROUTING(x) + #endif // Avoid spurious warnings // Arduino DUE is arm and uses traditional PROGMEM constructs From 414192fdea4053df7d158d3c24e05f578625201d Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 1 Oct 2014 01:34:39 -0600 Subject: [PATCH 038/105] A bit of cleanup and additions for RF24Mesh testing - No network ACKs for address request/response - cleanup some stuff --- RF24Network.cpp | 20 ++++++++------------ RF24Network.h | 17 +++++++++-------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 76657d0e..91a3dfd5 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -470,7 +470,6 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = First Payload, standard routing, 1=routed payload, 2=directRoute to host, 3=directRoute to Route { bool ok = false; - bool multicast = 0; // Radio ACK requested = 0 //const uint16_t fromAddress = frame_buffer[0] | (frame_buffer[1] << 8); // Throw it away if it's not a valid address @@ -478,9 +477,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F return false; //Load info into our conversion structure, and get the converted address info - conversion.send_node = to_node; - conversion.send_pipe = directTo; - conversion.multicast = multicast; + logicalToPhysicalStruct conversion = { to_node,directTo,0}; logicalToPhysicalAddress(&conversion); IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sending to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe)); @@ -491,7 +488,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F if(!ok){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe);); } - if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ){ + if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK && frame_buffer[6] != NETWORK_REQ_ADDRESS && frame_buffer[6] != NETWORK_ADDR_RESPONSE){ RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK @@ -499,12 +496,13 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F dynLen=8; - conversion.send_node=header.from_node; - conversion.send_pipe = TX_ROUTED; + //conversion.send_node=header.from_node; + //conversion.send_pipe = TX_ROUTED; + conversion = { header.from_node,TX_ROUTED,0}; logicalToPhysicalAddress(&conversion); //Write the data using the resulting physical address - write_to_pipe(conversion.send_node, conversion.send_pipe, multicast); + write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); dynLen=0; IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); ); @@ -517,7 +515,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F radio.startListening(); #endif - if( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 )){ + if( ok && conversion.send_node != to_node && (directTo==0 || directTo == 3 )){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ @@ -592,12 +590,11 @@ bool RF24Network::logicalToPhysicalAddress(logicalToPhysicalStruct *conversionIn bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) { bool ok = false; - + uint64_t out_pipe = pipe_address( node, pipe ); #if !defined (DUAL_HEAD_RADIO) // Open the correct pipe for writing. - // First, stop listening so we can talk radio.stopListening(); radio.openWritingPipe(out_pipe); @@ -760,7 +757,6 @@ bool RF24Network::is_valid_address( uint16_t node ) { result = false; IF_SERIAL_DEBUG_MINIMAL(printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node);); - printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node); break; } node >>= 3; diff --git a/RF24Network.h b/RF24Network.h index 296bcb53..2759beb5 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -32,26 +32,27 @@ #define MIN_USER_DEFINED_HEADER_TYPE 0 #define MAX_USER_DEFINED_HEADER_TYPE 127 -/** System Discard Types */ + #define NETWORK_ACK_REQUEST 128 #define NETWORK_ACK 129 +#define NETWORK_REQ_ADDRESS 151 +#define NETWORK_ADDR_RESPONSE 152 + /**System-Sub Types (0-255)*/ //#define NETWORK_REQ_STREAM 11; #define NETWORK_POLL 130 -/** System retained types */ #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 #define NETWORK_LAST_FRAGMENT 150 -/* System retained - response messages */ -#define NETWORK_REQ_ADDRESS 151 -#define NETWORK_ADDR_RESPONSE 152 + + /** Defines for handling written payloads */ #define TX_NORMAL 0 #define TX_ROUTED 1 -#define USER_TX_TO_PHYSICAL_ADDRESS 2 -#define USER_TX_TO_LOGICAL_ADDRESS 3 +#define USER_TX_TO_PHYSICAL_ADDRESS 2 //no network ACK +#define USER_TX_TO_LOGICAL_ADDRESS 3 // network ACK #define USER_TX_MULTICAST 4 /** System defines */ @@ -407,7 +408,7 @@ class RF24Network uint16_t send_node; uint8_t send_pipe; bool multicast; - }conversion; + }; bool logicalToPhysicalAddress(logicalToPhysicalStruct *conversionInfo); From e7fd5a723d43149b4cfb4df2e543cd2fb2fbcac6 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 4 Oct 2014 13:16:24 -0600 Subject: [PATCH 039/105] Minor changes for auto-addressing reqs - System messages result in immediate return from update() - Write address responses multiple times when routed - Network ACK only on address responses --- RF24Network.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 91a3dfd5..2ffb49c0 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -127,7 +127,7 @@ uint8_t RF24Network::update(void) if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Network ACK Rcvd \n")); ); returnVal = NETWORK_ACK; - continue; + return NETWORK_ACK; } // Add it to the buffer of frames for us if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; @@ -137,6 +137,8 @@ uint8_t RF24Network::update(void) header.to_node = requester; //header.from_node = node_address; write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //printf("fwd addr resp to 0%o , this node: 0%o \n", requester,node_address); continue; @@ -159,8 +161,8 @@ uint8_t RF24Network::update(void) return returnVal; }else{ enqueue(); - return returnVal; - } + return NETWORK_REQ_ADDRESS; + } } enqueue(); @@ -488,16 +490,12 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F if(!ok){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe);); } - if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK && frame_buffer[6] != NETWORK_REQ_ADDRESS && frame_buffer[6] != NETWORK_ADDR_RESPONSE){ + if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK && frame_buffer[6] != NETWORK_REQ_ADDRESS){ RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK - header.to_node = header.from_node; // Change the 'to' address to the 'from' address - - dynLen=8; - - //conversion.send_node=header.from_node; - //conversion.send_pipe = TX_ROUTED; + header.to_node = header.from_node; // Change the 'to' address to the 'from' address + dynLen=8; conversion = { header.from_node,TX_ROUTED,0}; logicalToPhysicalAddress(&conversion); @@ -515,7 +513,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F radio.startListening(); #endif - if( ok && conversion.send_node != to_node && (directTo==0 || directTo == 3 )){ + if( ok && conversion.send_node != to_node && (directTo==0 || directTo==3) && frame_buffer[6] != NETWORK_REQ_ADDRESS){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ @@ -710,8 +708,7 @@ uint16_t RF24Network::addressOfPipe( uint16_t node, uint8_t pipeNo ) m>>=1; //Shift to the right i++; //Count the # of increments } - - return node | (pipeNo << i); + return node | (pipeNo << i); } /******************************************************************/ From c73eabc438282da66f71b52883e8efabf92ee5b6 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 5 Oct 2014 22:18:06 -0600 Subject: [PATCH 040/105] Fix: Corrupt payloads Add: Address Confirm - Modify dynamic payload handling on AVR to prevent corrupt payloads in certain cases - Add address confirmation type NETWORK_ADDR_CONFIRM - Move frame_buffer to public to allow system type messages to be passed directly via the frame buffer --- RF24Network.cpp | 83 ++++++++++++++------------------- RF24Network.h | 10 +++- RPi/RF24Network/RF24Network.cpp | 25 ++++------ RPi/RF24Network/RF24Network.h | 6 ++- 4 files changed, 56 insertions(+), 68 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 2ffb49c0..66f1dace 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -106,6 +106,10 @@ uint8_t RF24Network::update(void) while ( radio.isValid() && radio.available())//&pipe_num) ) { + + dynLen = radio.getDynamicPayloadSize(); + if(!dynLen){delay(5);continue;} + // Dump the payloads until we've gotten everything // Fetch the payload, and see if this was the last one. radio.read( frame_buffer, sizeof(frame_buffer) ); @@ -124,51 +128,35 @@ uint8_t RF24Network::update(void) uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ - if(res == NETWORK_ACK){ // If received a routing payload, (Network ACK) discard it, and indicate what it was. - IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Network ACK Rcvd \n")); ); - returnVal = NETWORK_ACK; - return NETWORK_ACK; - } // Add it to the buffer of frames for us - if(header.type == NETWORK_ADDR_RESPONSE ){ - uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; - requester |= frame_buffer[9] << 8; - - if(requester != node_address){ - header.to_node = requester; - //header.from_node = node_address; - write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //printf("fwd addr resp to 0%o , this node: 0%o \n", requester,node_address); - continue; - } - } - if(header.type == NETWORK_REQ_ADDRESS && node_address){ - //RF24NetworkHeader reqHeader = header; - header.from_node = node_address; - header.to_node = 0; - write(header.to_node,TX_NORMAL); - //printf("fwd addr req\n"); + + if(res == NETWORK_ACK || (res == NETWORK_REQ_ADDRESS && !node_address) || res == NETWORK_ADDR_CONFIRM ){ + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); + return res; + } + if(header.type == NETWORK_ADDR_RESPONSE ){ + uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; + requester |= frame_buffer[9] << 8; + if(requester != node_address){ + header.to_node = requester; + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + delay(50); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + //printf("Fwd add response to 0%o\n",requester); continue; } - - // This ensures that all address requests are only placed at the front of the queue - if( header.type == NETWORK_REQ_ADDRESS ){ - - if(available()){ - //printf("Dropped address request\n"); - return returnVal; - }else{ - enqueue(); - return NETWORK_REQ_ADDRESS; - } } + if(header.type == NETWORK_REQ_ADDRESS && node_address){ + //printf("Fwd add req to 0\n"); + header.from_node = node_address; + header.to_node = 0; + write(header.to_node,TX_NORMAL); + continue; + } + enqueue(); }else{ - dynLen = radio.getDynamicPayloadSize(); - if(!dynLen){continue;} + #if defined (RF24NetworkMulticast) if( header.to_node == 0100){ if(header.id != lastMultiMessageID){ @@ -178,13 +166,11 @@ uint8_t RF24Network::update(void) } if(header.type == NETWORK_POLL ){ + //Serial.println("Send poll"); header.to_node = header.from_node; - header.from_node = node_address; - - delay(node_address%5); + header.from_node = node_address; + delay((node_address%5)*5); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //Serial.println("send poll"); continue; } @@ -194,7 +180,7 @@ uint8_t RF24Network::update(void) else{ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); ); } - }else{ + }else{ write(header.to_node,1); //Send it on, indicate it is a routed payload } #else @@ -495,14 +481,14 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK header.to_node = header.from_node; // Change the 'to' address to the 'from' address - dynLen=8; + //dynLen=8; conversion = { header.from_node,TX_ROUTED,0}; logicalToPhysicalAddress(&conversion); //Write the data using the resulting physical address write_to_pipe(conversion.send_node, conversion.send_pipe, conversion.multicast); - dynLen=0; + //dynLen=0; IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); ); } @@ -597,7 +583,8 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) radio.stopListening(); radio.openWritingPipe(out_pipe); size_t wLen = dynLen ? dynLen: frame_size; - radio.writeFast(frame_buffer, wLen,multicast); + radio.writeFast(&frame_buffer, wLen,multicast); + dynLen=0; ok = radio.txStandBy(txTimeout); diff --git a/RF24Network.h b/RF24Network.h index 2759beb5..335afb3d 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -37,6 +37,7 @@ #define NETWORK_ACK 129 #define NETWORK_REQ_ADDRESS 151 #define NETWORK_ADDR_RESPONSE 152 +#define NETWORK_ADDR_CONFIRM 153 /**System-Sub Types (0-255)*/ //#define NETWORK_REQ_STREAM 11; @@ -390,6 +391,7 @@ class RF24Network #endif uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); bool is_valid_address( uint16_t node ); + private: bool write(uint16_t, uint8_t directTo); @@ -423,7 +425,8 @@ class RF24Network 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 */ const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); - uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ + + #if defined RF24TINY uint8_t frame_queue[3*frame_size]; /**< Space for a small set of frames that need to be delivered to the app layer */ #else @@ -431,10 +434,13 @@ class RF24Network #endif 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 */ - + +public: + uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ }; diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index d2b9b3ee..60673c24 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -132,10 +132,10 @@ uint8_t RF24Network::update(void) { // Is this for us? if ( header.to_node == node_address ) { - if (header.type == NETWORK_ACK) { - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: Network ACK Rcvd\n"));); - returnVal = NETWORK_ACK; - continue; + + if (header.type == NETWORK_ACK || (header.type == NETWORK_REQ_ADDRESS && !node_address) || header.type == NETWORK_ADDR_CONFIRM ) { + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),res);); + return header.type; } if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; @@ -143,32 +143,23 @@ uint8_t RF24Network::update(void) { if(requester != node_address){ header.to_node = requester; - //header.from_node = node_address; memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); + delay(50); + write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); //printf("fwd addr resp to 0%o , this node: 0%o\n", requester,node_address); continue; } } if(header.type == NETWORK_REQ_ADDRESS && node_address){ - //RF24NetworkHeader reqHeader = header; + // printf("Fwd add req to 0\n"); header.from_node = node_address; header.to_node = 0; memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); write(header.to_node,TX_NORMAL); - //printf("fwd addr req\n"); continue; } - // This ensures that all address requests are only placed at the front of the queue - if( header.type == NETWORK_REQ_ADDRESS ){ - if(available()){ - printf("Dropped address request\n"); - return returnVal; - }else{ - enqueue(frame); - return returnVal; - } - } + enqueue(frame); if (radio.rxFifoFull()) { diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index a1394f4f..3cc9482c 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -63,6 +63,7 @@ /* System retained - response messages */ #define NETWORK_REQ_ADDRESS 151 #define NETWORK_ADDR_RESPONSE 152 +#define NETWORK_ADDR_CONFIRM 153 /** Defines for handling written payloads */ #define TX_NORMAL 0 @@ -345,6 +346,7 @@ class RF24Network { bool is_valid_address( uint16_t node ); uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); + protected: //void open_pipes(void); //uint16_t find_node( uint16_t current_node, uint16_t target_node ); @@ -377,7 +379,6 @@ uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ uint8_t frame_size; /**< How large is each frame over the air */ const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); - uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ std::queue frame_queue; std::map, RF24NetworkFrame> frameFragmentsCache; @@ -388,6 +389,9 @@ uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); bool noListen; //FIXME uint32_t lastWriteTime; //FIXME +public: +uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ + }; /** From 1560be5ead2c525469b14e36c68bf780fa001331 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 8 Oct 2014 16:41:37 -0600 Subject: [PATCH 041/105] Major changes - Network ACKs, Fixes Major Change: Changes to message types will require re-installation on ALL NODES Enable no-network-ack for certain message types. Users can control whether then network will send an ACK response for routed payloads. Message types 1 through 64 will NOT receive a network ACK. This is useful when expecting a response from the recipient, as an ACK is not always needed. - Fix: bad dynamic payload handling on Arduino (resulted in corrupt/messed up responses) - Fix: debug on RPi - Change network response types (requires re-install on all nodes) - Add network_ping system message type --- RF24Network.cpp | 15 ++- RF24Network.h | 190 +++++++++++++++++++------------- RPi/RF24Network/RF24Network.cpp | 17 ++- RPi/RF24Network/RF24Network.h | 30 +++-- 4 files changed, 155 insertions(+), 97 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 66f1dace..a262f23b 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -133,6 +133,10 @@ uint8_t RF24Network::update(void) IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); return res; } + if(res == NETWORK_PING){ + returnVal = NETWORK_PING; + continue; + } if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; requester |= frame_buffer[9] << 8; @@ -323,7 +327,8 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ - + dynLen=sizeof(RF24NetworkHeader)+len; + dynLen=min(dynLen,MAX_FRAME_SIZE); #if defined (DISABLE_FRAGMENTATION) return _write(header,message,len,writeDirect); #else @@ -476,7 +481,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F if(!ok){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu: MAC Send fail to 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe);); } - if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK && frame_buffer[6] != NETWORK_REQ_ADDRESS){ + if( directTo == TX_ROUTED && ok && conversion.send_node == to_node && frame_buffer[6] > 64 && frame_buffer[6] < 192){ RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK @@ -491,7 +496,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F //dynLen=0; IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); ); } - + dynLen=0; // if(!ok){ printf_P(PSTR("%lu: MAC No Ack from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } #if !defined (DUAL_HEAD_RADIO) @@ -499,7 +504,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F radio.startListening(); #endif - if( ok && conversion.send_node != to_node && (directTo==0 || directTo==3) && frame_buffer[6] != NETWORK_REQ_ADDRESS){ + if( ok && conversion.send_node != to_node && (directTo==0 || directTo==3) && frame_buffer[6] > 64 && frame_buffer[6] < 192){ uint32_t reply_time = millis(); while( update() != NETWORK_ACK){ if(millis() - reply_time > routeTimeout){ @@ -584,7 +589,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) radio.openWritingPipe(out_pipe); size_t wLen = dynLen ? dynLen: frame_size; radio.writeFast(&frame_buffer, wLen,multicast); - dynLen=0; + ok = radio.txStandBy(txTimeout); diff --git a/RF24Network.h b/RF24Network.h index 335afb3d..0cfef132 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -32,21 +32,29 @@ #define MIN_USER_DEFINED_HEADER_TYPE 0 #define MAX_USER_DEFINED_HEADER_TYPE 127 - -#define NETWORK_ACK_REQUEST 128 -#define NETWORK_ACK 129 -#define NETWORK_REQ_ADDRESS 151 -#define NETWORK_ADDR_RESPONSE 152 -#define NETWORK_ADDR_CONFIRM 153 - - /**System-Sub Types (0-255)*/ - //#define NETWORK_REQ_STREAM 11; -#define NETWORK_POLL 130 +/** + * Network Response Types + * The network will determine whether to automatically acknowledge payloads based on their type + * For User types (1-127) 1-64 will NOT be acknowledged + * For System types (128-255) 192 through 255 will NOT be acknowledged + */ +// ACK Response Types +#define NETWORK_ADDR_RESPONSE 128 +#define NETWORK_ADDR_CONFIRM 129 +#define NETWORK_PING 130 #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 #define NETWORK_LAST_FRAGMENT 150 +// NO ACK Response Types +#define NETWORK_ACK_REQUEST 192 +#define NETWORK_ACK 193 +#define NETWORK_POLL 194 +#define NETWORK_REQ_ADDRESS 195 + + + /** Defines for handling written payloads */ @@ -56,12 +64,12 @@ #define USER_TX_TO_LOGICAL_ADDRESS 3 // network ACK #define USER_TX_MULTICAST 4 + /** System defines */ #define MAX_FRAME_SIZE 32 #define MAX_PAYLOAD_SIZE 1500 - class RF24; /** @@ -74,6 +82,12 @@ 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 */ + /** + * Message Types: + * User message types 1 through 64 will NOT be acknowledged by the network. Message types 65 through 127 will receive a network ACK.
+ * System message types 192 through 255 will NOT be acknowledged by the network. Message types 128 through 192 will receive a network ACK. + * When requesting a response from another node, for example, a network ACK is not required, and will add extra traffic to the network. + */ 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 */ @@ -125,6 +139,7 @@ struct RF24NetworkHeader * Frame structure for each message * * The frame put over the air consists of a header and a message payload + * @note Frames are handled internally for fragmented payloads. Use RF24NetworkHeader */ @@ -209,7 +224,7 @@ class RF24Network */ void begin(uint8_t _channel, uint16_t _node_address ); - void failures(uint32_t *_fails, uint32_t *_ok); + /** * Main layer loop * @@ -262,66 +277,7 @@ class RF24Network * @return Whether the message was successfully received */ bool write(RF24NetworkHeader& header,const void* message, size_t len); - bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); - /** - * Send a multicast message to multiple nodes at once - * Allows messages to be rapidly broadcast through the network - * - * Multicasting is arranged in levels, with all nodes on the same level listening to the same address - * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 - * @see multicastLevel - * @param message Pointer to memory where the message is located - * @param len The size of the message - * @param level Multicast level to broadcast to - * @return Whether the message was successfully received - */ - - bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); - -/** - * 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:
- * 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 @@ -332,7 +288,7 @@ class RF24Network /** * 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. + * @note Not working on RPi. Radios will share MISO, MOSI and SCK pins, but require separate CE,CS pins. * @code * RF24 radio(7,8); * RF24 radio1(4,5); @@ -354,19 +310,44 @@ class RF24Network * */ - uint32_t txTimeout; + uint32_t txTimeout; /**< Network timeout value */ /** - * @note: Optimization: This new value defaults to 200 milliseconds. * This only affects payloads that are routed by one or more nodes. * This specifies how long to wait for an ack from across the network. * Radios routing directly to their parent or children nodes do not * utilize this value. */ - uint16_t routeTimeout; + uint16_t routeTimeout; /**< Timeout for routed payloads */ + + /** + * Return the number of failures and successes for all transmitted payloads, routed or sent directly + * @code + * bool fails, success; + * network.failures(&fails,&success); + * @endcode + * + */ + void failures(uint32_t *_fails, uint32_t *_ok); #if defined (RF24NetworkMulticast) + + /** + * Send a multicast message to multiple nodes at once + * Allows messages to be rapidly broadcast through the network + * + * Multicasting is arranged in levels, with all nodes on the same level listening to the same address + * Levels are assigned by network level ie: nodes 01-05: Level 1, nodes 011-055: Level 2 + * @see multicastLevel + * @param message Pointer to memory where the message is located + * @param len The size of the message + * @param level Multicast level to broadcast to + * @return Whether the message was successfully received + */ + + bool multicast(RF24NetworkHeader& header,const void* message, size_t len, uint8_t level); + /** * By default, multicast addresses are divided into levels. Nodes 1-5 share a multicast address, * nodes n1-n5 share a multicast address, and nodes n11-n55 share a multicast address. This option @@ -389,7 +370,66 @@ class RF24Network #endif + + /** + * Writes a direct payload. This allows routing or sending messages outside of the usual routing paths. + * The same as write, but a physical address is specified as the last option. + * The payload will be written to the physical address, and routed as necessary by the recipient + */ + bool write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect); + + /** + * 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:
+ * 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; + + /** + * Provided a node address and a pipe number, will return the RF24Network address of that child pipe for that node + */ uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); + + /** + * @note Addresses are specified in octal: 011, 034 + * @return True if a supplied address is valid + */ bool is_valid_address( uint16_t node ); private: diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 60673c24..dd9ed219 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -46,6 +46,7 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { node_address = _node_address; if ( ! radio.isValid() ) { + printf("invalid radio\n"); return; } radio.stopListening(); @@ -121,7 +122,7 @@ uint8_t RF24Network::update(void) { } // Throw it away if it's not a valid address - if ( !is_valid_address(header.to_node) ) { + if ( !is_valid_address(header.to_node) ){ continue; } @@ -134,9 +135,13 @@ uint8_t RF24Network::update(void) { if ( header.to_node == node_address ) { if (header.type == NETWORK_ACK || (header.type == NETWORK_REQ_ADDRESS && !node_address) || header.type == NETWORK_ADDR_CONFIRM ) { - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),res);); + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),header.type);); return header.type; } + if(header.type == NETWORK_PING){ + returnVal = NETWORK_PING; + continue; + } if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; requester |= frame_buffer[9] << 8; @@ -614,7 +619,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { #endif - if ( directTo == 1 && ok && conversion.send_node == to_node && frame_buffer[6] != NETWORK_ACK ) { + if ( directTo == 1 && ok && conversion.send_node == to_node && frame_buffer[6] > 64 && frame_buffer[6] < 192 ) { //RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); frame_buffer[6] = NETWORK_ACK; // Set the payload type to NETWORK_ACK @@ -629,21 +634,21 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) { //Write the data using the resulting physical address write_to_pipe(conversion.send_node, conversion.send_pipe, multicast); - IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,header.to_node);); + IF_SERIAL_DEBUG_ROUTING(printf("RT Route OK to 0%o ACK sent to 0%o\n",to_node,conversion.send_node);); } //if (!noListen ) { radio.startListening(); //} - if ( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 )) { + if ( ok && (conversion.send_node != to_node) && (directTo==0 || directTo == 3 ) && frame_buffer[6] > 64 && frame_buffer[6] < 192) { uint32_t reply_time = millis(); //Wait for NETWORK_ACK while( update() != NETWORK_ACK) { if (millis() - reply_time > routeTimeout) { ok = 0; - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),logicalAddress,send_node,send_pipe);); + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%u: RT Network ACK fail from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,conversion.send_node,conversion.send_pipe);); break; } } diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 3cc9482c..89ebeeb0 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -48,22 +48,30 @@ #define MIN_USER_DEFINED_HEADER_TYPE 0 #define MAX_USER_DEFINED_HEADER_TYPE 127 -/* System Discard Types */ -#define NETWORK_ACK_REQUEST 128 -#define NETWORK_ACK 129 -#define NETWORK_POLL 130 -/*System-Sub Types (0-255)*/ -#define NETWORK_REQ_STREAM 11; +/** + * Network Response Types + * The network will determine whether to automatically acknowledge payloads based on their type + * For User types (1-127) 1-64 will NOT be acknowledged + * For System types (128-255) 192 through 255 will NOT be acknowledged + */ +// ACK Response Types +#define NETWORK_ADDR_RESPONSE 128 +#define NETWORK_ADDR_CONFIRM 129 +#define NETWORK_PING 130 -/* System retained types */ #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 #define NETWORK_LAST_FRAGMENT 150 -/* System retained - response messages */ -#define NETWORK_REQ_ADDRESS 151 -#define NETWORK_ADDR_RESPONSE 152 -#define NETWORK_ADDR_CONFIRM 153 +// NO ACK Response Types +#define NETWORK_ACK_REQUEST 192 +#define NETWORK_ACK 193 +#define NETWORK_POLL 194 +#define NETWORK_REQ_ADDRESS 195 + + +/*System-Sub Types (0-255)*/ +//#define NETWORK_REQ_STREAM 11; /** Defines for handling written payloads */ #define TX_NORMAL 0 From d8dc1d64289b40f2ae9e19c7ccf578e20f4d01b6 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 8 Oct 2014 20:29:16 -0600 Subject: [PATCH 042/105] Add support for RF24Mesh address lookups Add support for RF24Mesh address lookups based on nodeID by returning the type when a lookup request is received. --- RF24Network.cpp | 2 +- RF24Network.h | 19 ++++++++----------- RPi/RF24Network/RF24Network.cpp | 5 ++--- RPi/RF24Network/RF24Network.h | 2 +- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index a262f23b..44b5b13f 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -129,7 +129,7 @@ uint8_t RF24Network::update(void) // Is this for us? if ( header.to_node == node_address ){ - if(res == NETWORK_ACK || (res == NETWORK_REQ_ADDRESS && !node_address) || res == NETWORK_ADDR_CONFIRM ){ + if( res == NETWORK_ADDR_LOOKUP || res == NETWORK_ACK || (res == NETWORK_REQ_ADDRESS && !node_address) || res == NETWORK_ADDR_CONFIRM ){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); return res; } diff --git a/RF24Network.h b/RF24Network.h index 0cfef132..6f716a0a 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -52,10 +52,7 @@ #define NETWORK_ACK 193 #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 - - - - +#define NETWORK_ADDR_LOOKUP 196 /** Defines for handling written payloads */ #define TX_NORMAL 0 @@ -84,11 +81,11 @@ struct RF24NetworkHeader uint16_t id; /**< Sequential message ID, incremented every message */ /** * Message Types: - * User message types 1 through 64 will NOT be acknowledged by the network. Message types 65 through 127 will receive a network ACK.
- * System message types 192 through 255 will NOT be acknowledged by the network. Message types 128 through 192 will receive a network ACK. - * When requesting a response from another node, for example, a network ACK is not required, and will add extra traffic to the network. + * User message types 1 through 64 will NOT be acknowledged by the network, while message types 65 through 127 will receive a network ACK. + * System message types 192 through 255 will NOT be acknowledged by the network. Message types 128 through 192 will receive a network ACK.
+ * When requesting a response from another node, for example, a network ACK is not required, and will add extra traffic to the network.

*/ - unsigned char type; /**< Type of the packet. 0-127 are user-defined types, 128-255 are reserved for system */ + 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 */ @@ -564,11 +561,11 @@ class RF24Network * @section Features Features * * Whats new? - * @note Fragmentation Support: (Sept 2014) - Raspberry Pi now supports fragmentation for very long messages, send as normal. Arduino, ATTiny will handle routing of fragmented messages, but cannot receive them properly - * @warning The latest updates to add fragmentation will require updating RF24Network and RF24 libraries on ALL devices - * + * @note Network Message Types Change: (Oct 8, 2014) Requires re-installation on all nodes
+ * New functionality: User message types 1 through 64 will not receive a network ack * * The layer provides: + * @li New (2014): Fragmentation/Re-assembly (RPi & Due only - Send only with Arduino) * @li New (2014): Network ACKs: Efficient acknowledgement of network-wide transmissions, via dynamic radio acks and network protocol acks. * @li New (2014): Updated addressing standard for optimal radio transmission. * @li New (2014): Extended timeouts and staggered timeout intervals. The new txTimeout variable allows fully automated extended timeout periods via auto-retry/auto-reUse of payloads. diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index dd9ed219..521b4e02 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -46,7 +46,6 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) { node_address = _node_address; if ( ! radio.isValid() ) { - printf("invalid radio\n"); return; } radio.stopListening(); @@ -122,7 +121,7 @@ uint8_t RF24Network::update(void) { } // Throw it away if it's not a valid address - if ( !is_valid_address(header.to_node) ){ + if ( !is_valid_address(header.to_node) ) { continue; } @@ -134,7 +133,7 @@ uint8_t RF24Network::update(void) { // Is this for us? if ( header.to_node == node_address ) { - if (header.type == NETWORK_ACK || (header.type == NETWORK_REQ_ADDRESS && !node_address) || header.type == NETWORK_ADDR_CONFIRM ) { + if ( header.type == NETWORK_ADDR_LOOKUP || header.type == NETWORK_ACK || (header.type == NETWORK_REQ_ADDRESS && !node_address) || header.type == NETWORK_ADDR_CONFIRM ) { IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),header.type);); return header.type; } diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 89ebeeb0..93f3a7b9 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -68,7 +68,7 @@ #define NETWORK_ACK 193 #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 - +#define NETWORK_ADDR_LOOKUP 196 /*System-Sub Types (0-255)*/ //#define NETWORK_REQ_STREAM 11; From 9bb97f4fd99d56a5462e602e02bd82c27b08cd81 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 9 Oct 2014 14:50:45 -0600 Subject: [PATCH 043/105] Simplify system msg handling, add addr release Simplify system msg handling: Network Update() will return immediately for all system message types, except for re-routed address requests or responses. This allows additional layers (such as RF24Mesh) to transmit and receive system payloads, and handle them directly from the frame_buffer, without interfering with user payloads. - Added NETWORK_ADDR_RELEASE type. Allows sleeping nodes to release their assigned address before going down. --- RF24Network.cpp | 25 ++++--------------------- RF24Network.h | 1 + 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 44b5b13f..52c7cc73 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -129,10 +129,6 @@ uint8_t RF24Network::update(void) // Is this for us? if ( header.to_node == node_address ){ - if( res == NETWORK_ADDR_LOOKUP || res == NETWORK_ACK || (res == NETWORK_REQ_ADDRESS && !node_address) || res == NETWORK_ADDR_CONFIRM ){ - IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); - return res; - } if(res == NETWORK_PING){ returnVal = NETWORK_PING; continue; @@ -157,6 +153,10 @@ uint8_t RF24Network::update(void) continue; } + if( res >127 ){ + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); + return res; + } enqueue(); }else{ @@ -193,23 +193,6 @@ uint8_t RF24Network::update(void) #endif } - - // NOT NEEDED anymore. Now all reading pipes are open to start. -#if 0 - // If this was for us, from one of our children, but on our listening - // pipe, it could mean that we are not listening to them. If so, open up - // and listen to their talking pipe - - if ( header.to_node == node_address && pipe_num == 0 && is_descendant(header.from_node) ) - { - uint8_t pipe = pipe_to_descendant(header.from_node); - radio.openReadingPipe(pipe,pipe_address(node_address,pipe)); - - // Also need to open pipe 1 so the system can get the full 5-byte address of the pipe. - radio.openReadingPipe(1,pipe_address(node_address,1)); - } -#endif - //} } return returnVal; } diff --git a/RF24Network.h b/RF24Network.h index 6f716a0a..abcd7283 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -53,6 +53,7 @@ #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 #define NETWORK_ADDR_LOOKUP 196 +#define NETWORK_ADDR_RELEASE 197 /** Defines for handling written payloads */ #define TX_NORMAL 0 From 4a66840f36010ea15aaa35f87a4b5e9a4e86a09e Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 10 Oct 2014 08:08:57 -0600 Subject: [PATCH 044/105] Forgot to add addr release for RPi --- RPi/RF24Network/RF24Network.cpp | 8 ++++---- RPi/RF24Network/RF24Network.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 521b4e02..d1b601e3 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -133,10 +133,6 @@ uint8_t RF24Network::update(void) { // Is this for us? if ( header.to_node == node_address ) { - if ( header.type == NETWORK_ADDR_LOOKUP || header.type == NETWORK_ACK || (header.type == NETWORK_REQ_ADDRESS && !node_address) || header.type == NETWORK_ADDR_CONFIRM ) { - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),header.type);); - return header.type; - } if(header.type == NETWORK_PING){ returnVal = NETWORK_PING; continue; @@ -164,6 +160,10 @@ uint8_t RF24Network::update(void) { continue; } + if(header.type>127){ + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),header.type);); + return header.type; + } enqueue(frame); if (radio.rxFifoFull()) { diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 93f3a7b9..2896a4f6 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -69,6 +69,7 @@ #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 #define NETWORK_ADDR_LOOKUP 196 +#define NETWORK_ADDR_RELEASE 197 /*System-Sub Types (0-255)*/ //#define NETWORK_REQ_STREAM 11; From 90d826b89515a7beebc13843704fbd03a886eae1 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 19 Oct 2014 12:59:50 -0600 Subject: [PATCH 045/105] Fix fragmentation, frag test changes - Fix fragmentation - broken by prev commit - Testing no network ack for payloads marked more_fragments --- RF24Network.cpp | 5 +++-- RF24Network.h | 8 +++++--- RPi/RF24Network/RF24Network.cpp | 34 +++++++++++++++++---------------- RPi/RF24Network/RF24Network.h | 8 ++++---- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 52c7cc73..fc4f91b8 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -154,9 +154,10 @@ uint8_t RF24Network::update(void) } if( res >127 ){ - IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: System payload rcvd %d\n"),res); ); + IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: System payload rcvd %d\n"),millis(),res); ); return res; - } + } + enqueue(); }else{ diff --git a/RF24Network.h b/RF24Network.h index abcd7283..8ec02664 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -40,7 +40,7 @@ */ // ACK Response Types #define NETWORK_ADDR_RESPONSE 128 -#define NETWORK_ADDR_CONFIRM 129 +//#define NETWORK_ADDR_CONFIRM 129 #define NETWORK_PING 130 #define NETWORK_FIRST_FRAGMENT 148 @@ -52,8 +52,10 @@ #define NETWORK_ACK 193 #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 -#define NETWORK_ADDR_LOOKUP 196 -#define NETWORK_ADDR_RELEASE 197 +//#define NETWORK_ADDR_LOOKUP 196 +//#define NETWORK_ADDR_RELEASE 197 + +#define NETWORK_MORE_FRAGMENTS_NACK 200 /** Defines for handling written payloads */ #define TX_NORMAL 0 diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index d1b601e3..8e3a9518 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -132,12 +132,11 @@ uint8_t RF24Network::update(void) { // Is this for us? if ( header.to_node == node_address ) { - if(header.type == NETWORK_PING){ returnVal = NETWORK_PING; continue; } - if(header.type == NETWORK_ADDR_RESPONSE ){ + if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; requester |= frame_buffer[9] << 8; @@ -161,17 +160,18 @@ uint8_t RF24Network::update(void) { } if(header.type>127){ - IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("RT: System payload rcvd %d\n"),header.type);); - return header.type; + IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%lu RT: System payload rcvd %d\n"),millis(),header.type);); + if( (header.type < 148 || header.type > 150) && header.type != NETWORK_MORE_FRAGMENTS_NACK){ + return header.type; + } } - enqueue(frame); + enqueue(frame); if (radio.rxFifoFull()) { printf(" fifo full\n"); } } else { //Frame if not for us - #if defined (RF24NetworkMulticast) if ( header.to_node == 0100) { @@ -216,7 +216,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { // This is sent to itself if (frame.header.from_node == node_address) { - bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT ); + bool isFragment = ( frame.header.type == NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_LAST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS_NACK); if (isFragment) { printf("Cannot enqueue multi-payload frames to self\n"); return false; @@ -225,7 +225,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { return true; } - if ((frame.header.reserved > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT)) ) { + if ((frame.header.reserved > 1 && (frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type== NETWORK_FIRST_FRAGMENT || frame.header.type == NETWORK_MORE_FRAGMENTS_NACK)) ) { //The received frame contains the a fragmented payload //Set the more fragments flag to indicate a fragmented frame @@ -238,7 +238,7 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { } else if ( frame.header.type == NETWORK_LAST_FRAGMENT) { //The last fragment flag indicates that we received the last fragment //Workaround/Hack: In this case the header.reserved does not count the number of fragments but is a copy of the type flags specified by the user application. - + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Last fragment with size %i Bytes and fragmentID '%i' received.\n\r",millis(),frame.message_size,frame.header.reserved);); //Append payload appendFragmentToFrame(frame); @@ -246,11 +246,15 @@ bool RF24Network::enqueue(RF24NetworkFrame frame) { IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET Enqueue assembled frame @%x "),millis(),frame_queue.size())); //Push the assembled frame in the frame_queue and remove it from cache + RF24NetworkFrame *f = &(frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ]); + // If total_fragments == 0, we are finished - if ( frame.header.to_node == node_address || (!frame.header.reserved && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ) { - frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + if ( frame.header.to_node == node_address && f->message_size > 0){//|| (!frame.header.reserved && frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) ) ) { + frame_queue.push( frameFragmentsCache[ std::make_pair(frame.header.from_node,frame.header.id) ] ); + } else { IF_SERIAL_DEBUG_MINIMAL( printf("%u: NET Dropped frame missing %d fragments\n",millis(),frame.total_fragments );); + } frameFragmentsCache.erase( std::make_pair(frame.header.from_node,frame.header.id) ); @@ -290,7 +294,7 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { if (frameFragmentsCache.count(std::make_pair(frame.header.from_node,frame.header.id)) == 0 ) { // This is the first of many fragments. - if (frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)) { + if (frame.header.type == NETWORK_FIRST_FRAGMENT || ( frame.header.type != NETWORK_MORE_FRAGMENTS_NACK && frame.header.type != NETWORK_MORE_FRAGMENTS && frame.header.type != NETWORK_LAST_FRAGMENT)) { // Decrement the stored total_fragments counter frame.header.reserved = frame.header.reserved-1; // Cache the fragment @@ -336,7 +340,6 @@ void RF24Network::appendFragmentToFrame(RF24NetworkFrame frame) { f->message_size += frame.message_size; //Increment message size f->header = frame.header; //Update header f->header.reserved--; //Update Total fragments - } } @@ -380,7 +383,6 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) // How much buffer size should we actually copy? bufsize = std::min(frame.message_size,maxlen); - memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); memcpy(message,frame.message_buffer,bufsize); @@ -490,7 +492,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (msgCount == 0) { fragmentHeader.type = NETWORK_FIRST_FRAGMENT; }else{ - fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + fragmentHeader.type = NETWORK_MORE_FRAGMENTS_NACK; //Set the more fragments flag to indicate a fragmented frame } } @@ -502,7 +504,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //uint8_t msg[32]; //Try to send the payload chunk with the copied header - bool ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + bool ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); if(!ok){ delay(100); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); if(!ok){ delay(150); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index 2896a4f6..ae4d8af7 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -57,7 +57,7 @@ */ // ACK Response Types #define NETWORK_ADDR_RESPONSE 128 -#define NETWORK_ADDR_CONFIRM 129 +//#define NETWORK_ADDR_CONFIRM 129 #define NETWORK_PING 130 #define NETWORK_FIRST_FRAGMENT 148 @@ -68,8 +68,9 @@ #define NETWORK_ACK 193 #define NETWORK_POLL 194 #define NETWORK_REQ_ADDRESS 195 -#define NETWORK_ADDR_LOOKUP 196 -#define NETWORK_ADDR_RELEASE 197 +//#define NETWORK_ADDR_LOOKUP 196 +//#define NETWORK_ADDR_RELEASE 197 +#define NETWORK_MORE_FRAGMENTS_NACK 200 /*System-Sub Types (0-255)*/ //#define NETWORK_REQ_STREAM 11; @@ -355,7 +356,6 @@ class RF24Network { bool is_valid_address( uint16_t node ); uint16_t addressOfPipe( uint16_t node,uint8_t pipeNo ); - protected: //void open_pipes(void); //uint16_t find_node( uint16_t current_node, uint16_t target_node ); From b2a7fd3b0dff446c0079e7741cd2f868116a679f Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 9 Nov 2014 18:22:47 -0600 Subject: [PATCH 046/105] Update readme for RF24 RPi/Arduino code merge --- RPi/readme.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RPi/readme.md b/RPi/readme.md index c88ac1f9..7c5d91b3 100644 --- a/RPi/readme.md +++ b/RPi/readme.md @@ -29,12 +29,10 @@ A. Make a directory to contain the RF24 and possibly RF24Network lib and enter i B. Clone the RF24 Repo - git clone https://github.com/tmrh20/RF24.git rtemp + git clone https://github.com/tmrh20/RF24.git RF24 -C. Copy the RPi library folder to the current directory, and delete the rest +C. Change to the new RF24 directory - mv rtemp/RPi/RF24 ./ - rm -r rtemp cd RF24 D. Build the library, and run an example file: From 31ba31452d0ff66fd1619baedbdaf4a78fbe4d9b Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 9 Nov 2014 18:26:26 -0600 Subject: [PATCH 047/105] Update other readme for RF24/RPi code merge Change install info --- RPi/RF24Network/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RPi/RF24Network/README.md b/RPi/RF24Network/README.md index c88ac1f9..7c5d91b3 100644 --- a/RPi/RF24Network/README.md +++ b/RPi/RF24Network/README.md @@ -29,12 +29,10 @@ A. Make a directory to contain the RF24 and possibly RF24Network lib and enter i B. Clone the RF24 Repo - git clone https://github.com/tmrh20/RF24.git rtemp + git clone https://github.com/tmrh20/RF24.git RF24 -C. Copy the RPi library folder to the current directory, and delete the rest +C. Change to the new RF24 directory - mv rtemp/RPi/RF24 ./ - rm -r rtemp cd RF24 D. Build the library, and run an example file: From f881426ffb72489b5b4202b55fe81b04f0482287 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 9 Nov 2014 20:13:11 -0600 Subject: [PATCH 048/105] Correct RF24 Core lib install info (code merge) --- RPi/RF24Network/README.md | 2 +- RPi/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RPi/RF24Network/README.md b/RPi/RF24Network/README.md index 7c5d91b3..d01d1eb9 100644 --- a/RPi/RF24Network/README.md +++ b/RPi/RF24Network/README.md @@ -38,7 +38,7 @@ C. Change to the new RF24 directory D. Build the library, and run an example file: sudo make install - cd examples + cd examples_RPi make sudo ./gettingstarted diff --git a/RPi/readme.md b/RPi/readme.md index 7c5d91b3..d01d1eb9 100644 --- a/RPi/readme.md +++ b/RPi/readme.md @@ -38,7 +38,7 @@ C. Change to the new RF24 directory D. Build the library, and run an example file: sudo make install - cd examples + cd examples_RPi make sudo ./gettingstarted From 333d3322a9bfdbc31ca617ab92a654f362a012be Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 12 Nov 2014 06:50:44 -0600 Subject: [PATCH 049/105] Fix compile errors on IDE 1.05 --- RF24Network.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index fc4f91b8..b54cf3af 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -471,7 +471,10 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F header.type = NETWORK_ACK; // Set the payload type to NETWORK_ACK header.to_node = header.from_node; // Change the 'to' address to the 'from' address //dynLen=8; - conversion = { header.from_node,TX_ROUTED,0}; + //conversion={header.from_node,TX_ROUTED,0}; + conversion.send_node = header.from_node; + conversion.send_pipe = TX_ROUTED; + conversion.multicast = 0; logicalToPhysicalAddress(&conversion); //Write the data using the resulting physical address From a6b3b5e9e290765ff91ff3dcb70b3dae87ee298f Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Nov 2014 19:24:21 -0600 Subject: [PATCH 050/105] Add fragmentation for Arduino, related changes - Enable fragmentation by default - Add EXTERNAL_DATA_TYPE - for use with external programs that use very large frames in their protocols - allows the external application to access the frame_buffers directly via a pointer, and use its own data caching mechanism - Redefine NETWORK_MORE_FRAGMENTS_NACK - Allow peek to return the frame size - Temporarily add peekData(); function for testing - Add fragmentation for Arduino - Modify enqueing - Change frame_size from static to variable - Proper use of dynamic payload sizes - Change queueing to fifo - Use a pointer for the header when sending fragmented frames --- RF24Network.cpp | 213 +++++++++++++++++++++++++------- RF24Network.h | 20 +-- RF24Network_config.h | 2 +- RPi/RF24Network/RF24Network.cpp | 27 ++-- RPi/RF24Network/RF24Network.h | 1 + 5 files changed, 199 insertions(+), 64 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index b54cf3af..e2975f48 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -107,12 +107,13 @@ uint8_t RF24Network::update(void) { - dynLen = radio.getDynamicPayloadSize(); - if(!dynLen){delay(5);continue;} + frame_size = radio.getDynamicPayloadSize(); + if(!frame_size){delay(5);continue;} // Dump the payloads until we've gotten everything // Fetch the payload, and see if this was the last one. - radio.read( frame_buffer, sizeof(frame_buffer) ); + //radio.read( frame_buffer, sizeof(frame_buffer) ); + radio.read( frame_buffer, frame_size ); // Read the beginning of the frame as the header RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); @@ -125,6 +126,8 @@ uint8_t RF24Network::update(void) continue; } + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),frame_size-sizeof(RF24NetworkHeader)); + uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ @@ -153,12 +156,15 @@ uint8_t RF24Network::update(void) continue; } - if( res >127 ){ + if( res >127 && ( res NETWORK_LAST_FRAGMENT && res != NETWORK_MORE_FRAGMENTS_NACK ) ){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: System payload rcvd %d\n"),millis(),res); ); return res; } - enqueue(); + if( enqueue(frame) == 2 ){ //External data received + Serial.println("ret ext"); + return EXTERNAL_DATA_TYPE; + } }else{ @@ -179,7 +185,7 @@ uint8_t RF24Network::update(void) continue; } - enqueue(); + enqueue(frame); lastMultiMessageID = header.id; } else{ @@ -200,18 +206,100 @@ uint8_t RF24Network::update(void) /******************************************************************/ -bool RF24Network::enqueue(void) +uint8_t RF24Network::enqueue(RF24NetworkFrame frame) { bool result = false; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Enqueue @%x "),millis(),next_frame-frame_queue)); - + + bool isFragment = frame.header.type >=148 && frame.header.type <=150 ? true : false; + + if(isFragment){ + RF24NetworkFrame *f = &frag_queue; + if(frame.header.type == NETWORK_FIRST_FRAGMENT){ + if(frame.header.reserved *24 >= MAX_PAYLOAD_SIZE){ + printf("Frag frame with %d frags exceeds MAX_PAYLOAD_SIZE\n",frame.header.reserved); + f->header.reserved = 0; + return 0; + } + printf("queue first, total frags %d\n",frame.header.reserved); + memcpy(f,&frame, sizeof(RF24NetworkFrame)); + f->total_fragments = f->header.reserved; + //Store the total size of the stored frame in message_size + f->message_size = frame.message_size; + f->header.reserved = f->total_fragments - 1; + //frag_ptr = [0]; + //frag_ptr += frame.message_size + sizeof(RF24NetworkHeader)+2; + //printf("enq size %d\n",f->message_size); + }else + if(frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_MORE_FRAGMENTS_NACK){ + //printf("queue more\n"); + if(frame.header.reserved != f->header.reserved){ + printf("Dropped out of order frame %d expected %d\n",frame.header.reserved,f->header.reserved); + f->header.reserved = 0; + return 0; + } + memcpy( f->message_buffer+f->message_size, &frame.message_buffer, frame.message_size); + f->message_size += frame.message_size; + f->header.reserved--; + //frag_ptr += frame.message_size; + //printf("enq size %d\n",f->message_size); + }else + if(frame.header.type == NETWORK_LAST_FRAGMENT){ + + if(f->header.reserved != 1 || f->header.id != frame.header.id){ + printf("Dropped out of order frame frag %d header id %d expected last (1) \n",frame.header.reserved,frame.header.id); + f->header.reserved = 0; + return 0; + } + f->header.reserved = 0; + printf("queue last - id: %d\n",frame.header.id); + memcpy(f->message_buffer+f->message_size,&frame.message_buffer, frame.message_size); + + //uint8_t numFrags = (f->message_size + 11) / 24 + 1; + + f->message_size += frame.message_size; + //Frame assembly complete, copy to main buffer if OK + + //if( numFrags == f->total_fragments ){ + //size_t place = sizeof(RF24NetworkHeader)+f->message_size + 2; +if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_PAYLOAD_SIZE) + { + //Set the type on the incoming message header, as well as the received message + frame.header.type = frame.header.reserved; + f->header.type = frame.header.reserved; + + if(frame.header.type == EXTERNAL_DATA_TYPE){ + frag_ptr = f; + return 2; + }else{ + memcpy(next_frame,f,sizeof(RF24NetworkHeader)+f->message_size + 3); + //next_frame+=place; + //memcpy( next_frame, &f->total_fragments, sizeof(f->total_fragments) ); + next_frame += f->message_size + 11; + } + printf("enq size %d\n",f->message_size); + //}else{ + // printf("mismatch %d %d\n",f->total_fragments,numFrags); + //} + } + + } + + + }else // Copy the current frame into the frame queue - if ( next_frame < frame_queue + sizeof(frame_queue) ) + if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_FRAME_SIZE) { - memcpy(next_frame,frame_buffer, frame_size ); - next_frame += frame_size; - + //memcpy(next_frame,frame_buffer, frame_size ); + if(frame.header.type == EXTERNAL_DATA_TYPE){ + frag_ptr = (RF24NetworkFrame*)next_frame; + return 2; + } + memcpy(next_frame,&frame, frame.message_size + 11); + next_frame += frame.message_size + 11; + //printf("enq %d\n",next_frame-frame_queue); + // printf("fs %d\n",frame.message_size); result = true; IF_SERIAL_DEBUG(printf_P(PSTR("ok\n\r"))); } @@ -242,14 +330,22 @@ uint16_t RF24Network::parent() const } /******************************************************************/ - -void RF24Network::peek(RF24NetworkHeader& header) +uint8_t RF24Network::peekData(){ + + return frame_queue[0]; +} +size_t RF24Network::peek(RF24NetworkHeader& header) { if ( available() ) { // Copy the next available frame from the queue into the provided buffer - memcpy(&header,next_frame-frame_size,sizeof(RF24NetworkHeader)); + //memcpy(&header,next_frame-frame_size,sizeof(RF24NetworkHeader)); + memcpy(&header,frame_queue,sizeof(RF24NetworkHeader)); + //next_frame=frame_queue; + RF24NetworkFrame& frame = * reinterpret_cast(frame_queue); + return frame.message_size; } + return 0; } /******************************************************************/ @@ -260,22 +356,41 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { + RF24NetworkFrame& frame = * reinterpret_cast(frame_queue); + //printf("siz %d\n",frame.message_size); + //RF24NetworkFrame frame = RF24NetworkFrame(header,frame_queue+sizeof(RF24NetworkHeader)+2,fSize); + //RF24NetworkFrame& frame = * reinterpret_cast(frame_queue); + + //Maybe change to memcpy + header = frame.header; // Move the pointer back one in the queue - next_frame -= frame_size; - uint8_t* frame = next_frame; - - memcpy(&header,frame,sizeof(RF24NetworkHeader)); - + //next_frame -= frame_size; + //uint8_t* frame = next_frame; +//printf("RD %d\n",frame.message_size); if (maxlen > 0) { + + memcpy(message,frame.message_buffer,frame.message_size); + IF_SERIAL_DEBUG(printf("%lu: FRG message size %d\n",millis(),frame.message_size);); + //IF_SERIAL_DEBUG(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < frame.message_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + size_t len = frame.message_size; + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); // How much buffer size should we actually copy? - bufsize = min(maxlen,frame_size-sizeof(RF24NetworkHeader)); - + //bufsize = min(maxlen,frame_size-sizeof(RF24NetworkHeader)); + // printf("RD %d\n",frame.message_size); // Copy the next available frame from the queue into the provided buffer - memcpy(message,frame+sizeof(RF24NetworkHeader),bufsize); + + } + + //memcpy(&frame_queue[0],frame_queue+frame.message_size+sizeof(RF24NetworkHeader),frame.message_size+sizeof(RF24NetworkHeader)); + next_frame-=frame.message_size+sizeof(RF24NetworkHeader)+3; + //next_frame=frame_queue; + + if(next_frame > frame_queue){ + memmove(frame_queue,frame_queue+frame.message_size+sizeof(RF24NetworkHeader)+3,next_frame-frame_queue); } - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); + //IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); } return bufsize; @@ -311,8 +426,8 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le } /******************************************************************/ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len, uint16_t writeDirect){ - dynLen=sizeof(RF24NetworkHeader)+len; - dynLen=min(dynLen,MAX_FRAME_SIZE); + frame_size=sizeof(RF24NetworkHeader)+len; + frame_size=min(frame_size,MAX_FRAME_SIZE); #if defined (DISABLE_FRAGMENTATION) return _write(header,message,len,writeDirect); #else @@ -344,17 +459,17 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le while (fragment_id > 0) { //Copy and fill out the header - RF24NetworkHeader fragmentHeader = header; - fragmentHeader.reserved = fragment_id; + RF24NetworkHeader *fragmentHeader = &header; + fragmentHeader->reserved = fragment_id; if (fragment_id == 1) { - fragmentHeader.type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment - fragmentHeader.reserved = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. + fragmentHeader->type = NETWORK_LAST_FRAGMENT; //Set the last fragment flag to indicate the last fragment + fragmentHeader->reserved = header.type; //Workaroung/Hack: to transmit the user application defined header.type, save this variable in the header.fragment_id. } else { if (msgCount == 0) { - fragmentHeader.type = NETWORK_FIRST_FRAGMENT; + fragmentHeader->type = NETWORK_FIRST_FRAGMENT; }else{ - fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame + fragmentHeader->type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } } @@ -364,14 +479,21 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header - bool ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); - - if(!ok){ delay(100); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); - if(!ok){ delay(150); ok = _write(fragmentHeader,message+offset,fragmentLen,writeDirect); - } - } + //printf("frz %d\n",frame_size); + frame_size = sizeof(RF24NetworkHeader)+fragmentLen; + bool ok = _write(*fragmentHeader,message+offset,fragmentLen,writeDirect); + + //fragmentHeader->id++; + + uint8_t counter = 0; + /*while(!ok){ + fragmentHeader.id++; + ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + counter++; + if(counter >= 3){ break; } + }*/ //if (!noListen) { - delayMicroseconds(50); + // delayMicroseconds(50); //} if (!ok) { @@ -398,7 +520,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Return true if all the chunks where sent successfully //else return false - IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); + //IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return true; #endif //Fragmentation enabled @@ -426,8 +548,9 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself if ( header.to_node == node_address ){ + RF24NetworkFrame frame = RF24NetworkFrame(header,message,min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); // Just queue it in the received queue - return enqueue(); + return enqueue(frame); } // Otherwise send it out over the air if(writeDirect != 070){ @@ -483,7 +606,7 @@ bool RF24Network::write(uint16_t to_node, uint8_t directTo) // Direct To: 0 = F //dynLen=0; IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: Route OK to 0%o ACK sent to 0%o\n"),millis(),to_node,header.to_node); ); } - dynLen=0; + //dynLen=0; // if(!ok){ printf_P(PSTR("%lu: MAC No Ack from 0%o via 0%o on pipe %x\n\r"),millis(),to_node,send_node,send_pipe); } #if !defined (DUAL_HEAD_RADIO) @@ -574,9 +697,9 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // First, stop listening so we can talk radio.stopListening(); radio.openWritingPipe(out_pipe); - size_t wLen = dynLen ? dynLen: frame_size; - radio.writeFast(&frame_buffer, wLen,multicast); - + //size_t wLen = dynLen ? dynLen: frame_size; + radio.writeFast(&frame_buffer, frame_size,multicast); +//printf("enq size %d\n",frame_size); ok = radio.txStandBy(txTimeout); diff --git a/RF24Network.h b/RF24Network.h index 8ec02664..8805faa5 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -42,6 +42,7 @@ #define NETWORK_ADDR_RESPONSE 128 //#define NETWORK_ADDR_CONFIRM 129 #define NETWORK_PING 130 +#define EXTERNAL_DATA_TYPE 131 #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 @@ -55,7 +56,7 @@ //#define NETWORK_ADDR_LOOKUP 196 //#define NETWORK_ADDR_RELEASE 197 -#define NETWORK_MORE_FRAGMENTS_NACK 200 +#define NETWORK_MORE_FRAGMENTS_NACK 112 /** Defines for handling written payloads */ #define TX_NORMAL 0 @@ -250,7 +251,8 @@ class RF24Network * * @param[out] header The header (envelope) of the next message */ - void peek(RF24NetworkHeader& header); + size_t peek(RF24NetworkHeader& header); + uint8_t peekData(); /** * Read a message @@ -436,7 +438,7 @@ class RF24Network bool write(uint16_t, uint8_t directTo); bool write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ); - bool enqueue(void); + uint8_t enqueue(RF24NetworkFrame frame); bool is_direct_child( uint16_t node ); bool is_descendant( uint16_t node ); @@ -463,16 +465,19 @@ class RF24Network uint8_t multicast_level; #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 */ + //const static int frame_size = 32; /**< How large is each frame over the air */ + uint8_t frame_size; const static unsigned int max_frame_payload_size = MAX_FRAME_SIZE-sizeof(RF24NetworkHeader); #if defined RF24TINY - uint8_t frame_queue[3*frame_size]; /**< Space for a small set of frames that need to be delivered to the app layer */ + uint8_t frame_queue[3*MAX_FRAME_SIZE]; /**< Space for a small set of frames that need to be delivered to the app layer */ #else - 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 frame_queue[3*MAX_FRAME_SIZE]; /**< Space for a small set of frames that need to be delivered to the app layer */ #endif uint8_t* next_frame; /**< Pointer into the @p frame_queue where we should place the next received frame */ + //uint8_t frag_queue[MAX_PAYLOAD_SIZE + 11]; + RF24NetworkFrame frag_queue; uint16_t parent_node; /**< Our parent's node address */ @@ -480,7 +485,8 @@ class RF24Network uint16_t node_mask; /**< The bits which contain signfificant node address information */ public: - uint8_t frame_buffer[frame_size]; /**< Space to put the frame that will be sent/received over the air */ + RF24NetworkFrame *frag_ptr; + uint8_t frame_buffer[MAX_FRAME_SIZE]; /**< Space to put the frame that will be sent/received over the air */ }; diff --git a/RF24Network_config.h b/RF24Network_config.h index 91c9d7c0..8696e6ba 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -23,7 +23,7 @@ //#define DUAL_HEAD_RADIO //#define ENABLE_SLEEP_MODE #define RF24NetworkMulticast -#define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation +//#define DISABLE_FRAGMENTATION // Saves a bit of space by disabling fragmentation //#define SERIAL_DEBUG //#define SERIAL_DEBUG_MINIMAL diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 8e3a9518..86f12427 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -386,8 +386,9 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) memcpy(&header,&(frame.header),sizeof(RF24NetworkHeader)); memcpy(message,frame.message_buffer,bufsize); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message size %i\n",millis(),frame.message_size);); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG(printf("%u: FRG message size %i\n",millis(),frame.message_size);); + IF_SERIAL_DEBUG(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < bufsize; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + IF_SERIAL_DEBUG(printf_P(PSTR("%u: NET read %s\n\r"),millis(),header.toString())); frame_queue.pop(); @@ -492,7 +493,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le if (msgCount == 0) { fragmentHeader.type = NETWORK_FIRST_FRAGMENT; }else{ - fragmentHeader.type = NETWORK_MORE_FRAGMENTS_NACK; //Set the more fragments flag to indicate a fragmented frame + fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } } @@ -502,16 +503,19 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); //uint8_t msg[32]; - + //delay(5); //Try to send the payload chunk with the copied header + frame_size = sizeof(RF24NetworkHeader)+fragmentLen; bool ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - - if(!ok){ delay(100); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - if(!ok){ delay(150); ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - } - } + //header.id++; + uint8_t counter = 0; + /*while(!ok){ + ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + counter++; + if(counter >= 3){ break; } + }*/ //if (!noListen) { - delayMicroseconds(50); + //delayMicroseconds(50); //} if (!ok) { @@ -533,9 +537,10 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // Now, continue listening //radio.startListening(); + // int frag_delay = uint8_t(len/48); + // delay( std::min(frag_delay,20)); int frag_delay = uint8_t(len/48); delay( std::min(frag_delay,20)); - //Return true if all the chuncks where sent successfuly //else return false IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); diff --git a/RPi/RF24Network/RF24Network.h b/RPi/RF24Network/RF24Network.h index ae4d8af7..d2a7ba4f 100644 --- a/RPi/RF24Network/RF24Network.h +++ b/RPi/RF24Network/RF24Network.h @@ -59,6 +59,7 @@ #define NETWORK_ADDR_RESPONSE 128 //#define NETWORK_ADDR_CONFIRM 129 #define NETWORK_PING 130 +#define EXTERNAL_DATA_TYPE 131 #define NETWORK_FIRST_FRAGMENT 148 #define NETWORK_MORE_FRAGMENTS 149 From 1dd728229c632aed29c9d89a60f16e2cd3cacfd9 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Nov 2014 19:51:10 -0600 Subject: [PATCH 051/105] Set default max payload to 128bytes for Arduino Buffer sizes are now based on MAX_PAYLOAD_SIZE and MAX_FRAME_SIZE, minimize default memory usage for Arduino - This needs to be increased if using payloads larger than 128 bytes --- RF24Network.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24Network.h b/RF24Network.h index 8805faa5..07fc0c13 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -68,7 +68,7 @@ /** System defines */ #define MAX_FRAME_SIZE 32 -#define MAX_PAYLOAD_SIZE 1500 +#define MAX_PAYLOAD_SIZE 128 class RF24; From 840036b402fd5c9ffca5ff042df81d9de0a4ac06 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 20 Nov 2014 13:56:01 -0600 Subject: [PATCH 052/105] Fix compile error with IDE 1.0.5 - Fix compile error - some minor changes --- RF24Network.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index e2975f48..aa4a678d 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -162,7 +162,7 @@ uint8_t RF24Network::update(void) } if( enqueue(frame) == 2 ){ //External data received - Serial.println("ret ext"); + //Serial.println("ret ext"); return EXTERNAL_DATA_TYPE; } @@ -356,13 +356,13 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if ( available() ) { - RF24NetworkFrame& frame = * reinterpret_cast(frame_queue); + RF24NetworkFrame* frame = (RF24NetworkFrame*)&frame_queue; //printf("siz %d\n",frame.message_size); //RF24NetworkFrame frame = RF24NetworkFrame(header,frame_queue+sizeof(RF24NetworkHeader)+2,fSize); //RF24NetworkFrame& frame = * reinterpret_cast(frame_queue); //Maybe change to memcpy - header = frame.header; + header = frame->header; // Move the pointer back one in the queue //next_frame -= frame_size; //uint8_t* frame = next_frame; @@ -370,10 +370,10 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) if (maxlen > 0) { - memcpy(message,frame.message_buffer,frame.message_size); + memcpy(message,frame->message_buffer,frame->message_size); IF_SERIAL_DEBUG(printf("%lu: FRG message size %d\n",millis(),frame.message_size);); //IF_SERIAL_DEBUG(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < frame.message_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); - size_t len = frame.message_size; + size_t len = frame->message_size; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); // How much buffer size should we actually copy? //bufsize = min(maxlen,frame_size-sizeof(RF24NetworkHeader)); @@ -383,11 +383,11 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) } //memcpy(&frame_queue[0],frame_queue+frame.message_size+sizeof(RF24NetworkHeader),frame.message_size+sizeof(RF24NetworkHeader)); - next_frame-=frame.message_size+sizeof(RF24NetworkHeader)+3; + next_frame-=frame->message_size+sizeof(RF24NetworkHeader)+3; //next_frame=frame_queue; if(next_frame > frame_queue){ - memmove(frame_queue,frame_queue+frame.message_size+sizeof(RF24NetworkHeader)+3,next_frame-frame_queue); + memmove(frame_queue,frame_queue+frame->message_size+sizeof(RF24NetworkHeader)+3,next_frame-frame_queue); } //IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); @@ -481,7 +481,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Try to send the payload chunk with the copied header //printf("frz %d\n",frame_size); frame_size = sizeof(RF24NetworkHeader)+fragmentLen; - bool ok = _write(*fragmentHeader,message+offset,fragmentLen,writeDirect); + bool ok = _write(*fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); //fragmentHeader->id++; From bdc6dc283e76dd7c4f68bedcbfa780e77959aa19 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 21 Nov 2014 04:23:11 -0600 Subject: [PATCH 053/105] Fix broken routing on Arduino Bad logic broke routing --- RF24Network.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index aa4a678d..0f1f76d7 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -136,6 +136,7 @@ uint8_t RF24Network::update(void) returnVal = NETWORK_PING; continue; } + if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; requester |= frame_buffer[9] << 8; @@ -156,9 +157,12 @@ uint8_t RF24Network::update(void) continue; } - if( res >127 && ( res NETWORK_LAST_FRAGMENT && res != NETWORK_MORE_FRAGMENTS_NACK ) ){ + if( res >127 ){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: System payload rcvd %d\n"),millis(),res); ); - return res; + if( (header.type < 148 || header.type > 150) && header.type != NETWORK_MORE_FRAGMENTS_NACK){ + printf("Type %d\n",header.type); + return res; + } } if( enqueue(frame) == 2 ){ //External data received @@ -371,7 +375,7 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) { memcpy(message,frame->message_buffer,frame->message_size); - IF_SERIAL_DEBUG(printf("%lu: FRG message size %d\n",millis(),frame.message_size);); + IF_SERIAL_DEBUG(printf("%lu: FRG message size %d\n",millis(),frame->message_size);); //IF_SERIAL_DEBUG(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < frame.message_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); size_t len = frame->message_size; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); @@ -731,7 +735,7 @@ const char* RF24NetworkHeader::toString(void) const { static char buffer[45]; //snprintf_P(buffer,sizeof(buffer),PSTR("id %04x from 0%o to 0%o type %c"),id,from_node,to_node,type); - sprintf_P(buffer,PSTR("id %04x from 0%o to 0%o type %c"),id,from_node,to_node,type); + sprintf_P(buffer,PSTR("id %d from 0%o to 0%o type %d"),id,from_node,to_node,type); return buffer; } From 806209301bf04b51ce46d01593c55486f5593778 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Mon, 24 Nov 2014 21:16:06 -0600 Subject: [PATCH 054/105] Clean up a bit from recent changes - Change structure of RF24NetworkFrames - move total_payloads behind message, change message to a pointer instead of a fixed array size - Move frame/payload size defines to RF24Network_config.h - Enable Arduino to receive fragmented multicast payloads - Changes to frag re-assembly code - Remove non-debug print statements --- RF24Network.cpp | 244 ++++++++++++++++++++++++------------------- RF24Network.h | 73 ++++++++----- RF24Network_config.h | 7 +- 3 files changed, 190 insertions(+), 134 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 0f1f76d7..b44f0876 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -17,19 +17,21 @@ #endif uint16_t RF24NetworkHeader::next_id = 1; - +uint32_t RF24Network::nFails = 0; +uint32_t RF24Network::nOK = 0; uint64_t pipe_address( uint16_t node, uint8_t pipe ); #if defined (RF24NetworkMulticast) uint16_t levelToAddress( uint8_t level ); #endif bool is_valid_address( uint16_t node ); -uint32_t nFails = 0, nOK=0; -uint8_t dynLen = 0; +//uint32_t nFails = 0, nOK=0; +//uint8_t dynLen = 0; /******************************************************************/ #if !defined (DUAL_HEAD_RADIO) -RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue) { + } #else RF24Network::RF24Network( RF24& _radio, RF24& _radio1 ): radio(_radio), radio1(_radio1), next_frame(frame_queue) @@ -49,6 +51,9 @@ void RF24Network::begin(uint8_t _channel, uint16_t _node_address ) return; } + #if !defined (DISABLE_FRAGMENTATION) + //*frag_queue.message_buffer = &frag_queue_message_buffer[0]; + #endif radio.stopListening(); // Set up the radio the way we want it to look radio.setChannel(_channel); @@ -117,7 +122,6 @@ uint8_t RF24Network::update(void) // Read the beginning of the frame as the header RF24NetworkHeader& header = * reinterpret_cast(frame_buffer); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Received on %u %s\n\r"),millis(),pipe_num,header.toString())); IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(frame_buffer + sizeof(RF24NetworkHeader));printf_P(PSTR("%lu: NET message %04x\n\r"),millis(),*i)); @@ -126,8 +130,9 @@ uint8_t RF24Network::update(void) continue; } - RF24NetworkFrame frame = RF24NetworkFrame(header,frame_buffer+sizeof(RF24NetworkHeader),frame_size-sizeof(RF24NetworkHeader)); - + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_size-sizeof(RF24NetworkHeader),0); + frame.message_buffer = frame_buffer; + uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ @@ -136,7 +141,7 @@ uint8_t RF24Network::update(void) returnVal = NETWORK_PING; continue; } - + if(header.type == NETWORK_ADDR_RESPONSE ){ uint16_t requester = frame_buffer[8];// | frame_buffer[9] << 8; requester |= frame_buffer[9] << 8; @@ -160,21 +165,23 @@ uint8_t RF24Network::update(void) if( res >127 ){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("%lu MAC: System payload rcvd %d\n"),millis(),res); ); if( (header.type < 148 || header.type > 150) && header.type != NETWORK_MORE_FRAGMENTS_NACK){ - printf("Type %d\n",header.type); + //printf("Type %d\n",header.type); return res; } } if( enqueue(frame) == 2 ){ //External data received + #if defined (SERIAL_DEBUG_MINIMAL) //Serial.println("ret ext"); return EXTERNAL_DATA_TYPE; + #endif } }else{ #if defined (RF24NetworkMulticast) if( header.to_node == 0100){ - if(header.id != lastMultiMessageID){ + if(header.id != lastMultiMessageID || (header.type>=NETWORK_FIRST_FRAGMENT && header.type<=NETWORK_LAST_FRAGMENT)){ if(multicastRelay){ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: FWD multicast frame from 0%o to level %d\n"),header.from_node,multicast_level+1); ); write(levelToAddress(multicast_level)<<3,4); @@ -189,7 +196,10 @@ uint8_t RF24Network::update(void) continue; } - enqueue(frame); + if( enqueue(frame) == 2 ){ //External data received + //Serial.println("ret ext"); + return EXTERNAL_DATA_TYPE; + } lastMultiMessageID = header.id; } else{ @@ -215,103 +225,141 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) bool result = false; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Enqueue @%x "),millis(),next_frame-frame_queue)); - + +#if !defined ( DISABLE_FRAGMENTATION ) + bool isFragment = frame.header.type >=148 && frame.header.type <=150 ? true : false; if(isFragment){ - RF24NetworkFrame *f = &frag_queue; + + frag_queue.message_buffer=&frag_queue_message_buffer[0]; + if(frame.header.type == NETWORK_FIRST_FRAGMENT){ - if(frame.header.reserved *24 >= MAX_PAYLOAD_SIZE){ + if(frame.header.reserved *24 > MAX_PAYLOAD_SIZE){ + // TUN: + + //if(frame.header.reserved *24 > MAX_PAYLOAD_SIZE + 28){ +#if defined (SERIAL_DEBUG_FRAGMENTATION) printf("Frag frame with %d frags exceeds MAX_PAYLOAD_SIZE\n",frame.header.reserved); - f->header.reserved = 0; +#endif + frag_queue.header.reserved = 0; return 0; } - printf("queue first, total frags %d\n",frame.header.reserved); - memcpy(f,&frame, sizeof(RF24NetworkFrame)); - f->total_fragments = f->header.reserved; +#if defined (SERIAL_DEBUG_FRAGMENTATION) + printf("queue first, total frags %d\n",frame.header.reserved); +#endif + memcpy(&frag_queue,&frame,11); + memcpy(frag_queue.message_buffer,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); +#if defined (SERIAL_DEBUG_FRAGMENTATION_L2) + for(int i=0; imessage_size = frame.message_size; - f->header.reserved = f->total_fragments - 1; - //frag_ptr = [0]; - //frag_ptr += frame.message_size + sizeof(RF24NetworkHeader)+2; - //printf("enq size %d\n",f->message_size); + frag_queue.message_size = frame.message_size; + frag_queue.header.reserved = frag_queue.total_fragments - 1; + }else if(frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_MORE_FRAGMENTS_NACK){ - //printf("queue more\n"); - if(frame.header.reserved != f->header.reserved){ - printf("Dropped out of order frame %d expected %d\n",frame.header.reserved,f->header.reserved); - f->header.reserved = 0; + if(frame.header.reserved != frag_queue.header.reserved){ +#if defined (SERIAL_DEBUG_FRAGMENTATION) + printf("Dropped out of order frame %d expected %d\n",frame.header.reserved,frame.header.reserved); +#endif + frag_queue.header.reserved = 0; return 0; } - memcpy( f->message_buffer+f->message_size, &frame.message_buffer, frame.message_size); - f->message_size += frame.message_size; - f->header.reserved--; - //frag_ptr += frame.message_size; - //printf("enq size %d\n",f->message_size); + memcpy(frag_queue.message_buffer+frag_queue.message_size,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); + frag_queue.message_size += frame.message_size; + frag_queue.header.reserved--; + }else if(frame.header.type == NETWORK_LAST_FRAGMENT){ - if(f->header.reserved != 1 || f->header.id != frame.header.id){ + if(frag_queue.header.reserved != 1 || frag_queue.header.id != frame.header.id){ + #if defined (SERIAL_DEBUG_FRAGMENTATION) printf("Dropped out of order frame frag %d header id %d expected last (1) \n",frame.header.reserved,frame.header.id); - f->header.reserved = 0; + #endif + frag_queue.header.reserved = 0; return 0; } - f->header.reserved = 0; - printf("queue last - id: %d\n",frame.header.id); - memcpy(f->message_buffer+f->message_size,&frame.message_buffer, frame.message_size); - - //uint8_t numFrags = (f->message_size + 11) / 24 + 1; - - f->message_size += frame.message_size; + frag_queue.header.reserved = 0; + + memcpy(frag_queue.message_buffer+frag_queue.message_size,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); + + frag_queue.message_size += frame.message_size; +#if defined (SERIAL_DEBUG_FRAGMENTATION) + printf("fq 3: %d\n",frag_queue.message_size); +#endif +#if defined (SERIAL_DEBUG_FRAGMENTATION_L2) + for(int i=0; i< frag_queue.message_size;i++){ + Serial.println(frag_queue.message_buffer[i],HEX); + } +#endif + //Frame assembly complete, copy to main buffer if OK - //if( numFrags == f->total_fragments ){ - //size_t place = sizeof(RF24NetworkHeader)+f->message_size + 2; -if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_PAYLOAD_SIZE) - { - //Set the type on the incoming message header, as well as the received message - frame.header.type = frame.header.reserved; - f->header.type = frame.header.reserved; + if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_PAYLOAD_SIZE) + { + //Set the type on the incoming message header, as well as the received message + frame.header.type = frame.header.reserved; + frag_queue.header.type = frame.header.reserved; - if(frame.header.type == EXTERNAL_DATA_TYPE){ - frag_ptr = f; + if(frame.header.type == EXTERNAL_DATA_TYPE){ + frag_ptr = &frag_queue; return 2; - }else{ - memcpy(next_frame,f,sizeof(RF24NetworkHeader)+f->message_size + 3); - //next_frame+=place; - //memcpy( next_frame, &f->total_fragments, sizeof(f->total_fragments) ); - next_frame += f->message_size + 11; - } - printf("enq size %d\n",f->message_size); - //}else{ - // printf("mismatch %d %d\n",f->total_fragments,numFrags); - //} - } + }else{ + //memcpy(next_frame,&frag_queue,11+frag_queue.message_size); + + if( frag_queue.message_size < sizeof(frame_queue)-(next_frame-frame_queue)){ + memcpy(next_frame,&frag_queue,11); +// memcpy(next_frame+11,&frag_queue.message_buffer,frag_queue.message_size); + memcpy(next_frame+11,frag_queue.message_buffer,frag_queue.message_size); + next_frame += (11+frag_queue.message_size); + } + } + #if defined (SERIAL_DEBUG_FRAGMENTATION) + printf("enq size %d\n",frag_queue.message_size); + #endif - } + } + }//If last Fragment - }else + }else //else is not a fragment + #endif // Copy the current frame into the frame queue - if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_FRAME_SIZE) + if ( next_frame < frame_queue + sizeof(frame_queue) && frame.message_size <= MAX_FRAME_SIZE) { //memcpy(next_frame,frame_buffer, frame_size ); - if(frame.header.type == EXTERNAL_DATA_TYPE){ - frag_ptr = (RF24NetworkFrame*)next_frame; +#if !defined( DISABLE_FRAGMENTATION ) + frag_ptr = (RF24NetworkFrame*)&next_frame; +#endif + if(frame.header.type == EXTERNAL_DATA_TYPE){ return 2; } - memcpy(next_frame,&frame, frame.message_size + 11); - next_frame += frame.message_size + 11; - //printf("enq %d\n",next_frame-frame_queue); - // printf("fs %d\n",frame.message_size); + + memcpy(next_frame,&frame,11); + memcpy(next_frame+11,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); +#if defined (SERIAL_DEBUG_FRAGMENTATION) + for(int i=0; i(frame_queue); - - //Maybe change to memcpy - header = frame->header; - // Move the pointer back one in the queue - //next_frame -= frame_size; - //uint8_t* frame = next_frame; -//printf("RD %d\n",frame.message_size); + + RF24NetworkFrame *frame = (RF24NetworkFrame*)frame_queue; + memcpy(&header,frame_queue,11); + if (maxlen > 0) - { + { - memcpy(message,frame->message_buffer,frame->message_size); - IF_SERIAL_DEBUG(printf("%lu: FRG message size %d\n",millis(),frame->message_size);); - //IF_SERIAL_DEBUG(printf("%u: FRG message ",millis()); const char* charPtr = reinterpret_cast(message); for (size_t i = 0; i < frame.message_size; i++) { printf("%02X ", charPtr[i]); }; printf("\n\r")); + memcpy(message,&frame_queue[11],frame->message_size); + IF_SERIAL_DEBUG(printf("%lu: NET message size %d\n",millis(),frame->message_size);); + size_t len = frame->message_size; - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); - // How much buffer size should we actually copy? - //bufsize = min(maxlen,frame_size-sizeof(RF24NetworkHeader)); - // printf("RD %d\n",frame.message_size); - // Copy the next available frame from the queue into the provided buffer + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); } - - //memcpy(&frame_queue[0],frame_queue+frame.message_size+sizeof(RF24NetworkHeader),frame.message_size+sizeof(RF24NetworkHeader)); - next_frame-=frame->message_size+sizeof(RF24NetworkHeader)+3; - //next_frame=frame_queue; - - if(next_frame > frame_queue){ - memmove(frame_queue,frame_queue+frame->message_size+sizeof(RF24NetworkHeader)+3,next_frame-frame_queue); - } + next_frame-=frame->message_size+11; // + memmove(frame_queue,frame_queue+frame->message_size+11,sizeof(frame_queue)); //IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); } @@ -541,18 +572,17 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Sending %s\n\r"),millis(),header.toString())); if (len){ + memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,min(frame_size-sizeof(RF24NetworkHeader),len)); - //IF_SERIAL_DEBUG(const uint16_t* i = reinterpret_cast(message);printf_P(PSTR("%lu: NET message %08x\n\r"),millis(),*i)); - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); - - //IF_SERIAL_DEBUG( printf_P(PSTR("%lu: NET message "),millis()); const uint16_t* charPtr[] = reinterpret_cast(message); printf("%x\n",*charPtr) ); + IF_SERIAL_DEBUG(size_t tmpLen = len;printf_P(PSTR("%lu: NET message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(tmpLen--){ printf("%02x ",charPtr[tmpLen]);} printf_P(PSTR("\n\r") ) ); } // If the user is trying to send it to himself if ( header.to_node == node_address ){ - RF24NetworkFrame frame = RF24NetworkFrame(header,message,min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); + + RF24NetworkFrame frame = RF24NetworkFrame(header,len,0); // Just queue it in the received queue return enqueue(frame); } @@ -701,12 +731,8 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) // First, stop listening so we can talk radio.stopListening(); radio.openWritingPipe(out_pipe); - //size_t wLen = dynLen ? dynLen: frame_size; radio.writeFast(&frame_buffer, frame_size,multicast); -//printf("enq size %d\n",frame_size); - ok = radio.txStandBy(txTimeout); - - + ok = radio.txStandBy(txTimeout); #else radio1.openWritingPipe(out_pipe); radio1.writeFast(frame_buffer, frame_size); diff --git a/RF24Network.h b/RF24Network.h index 07fc0c13..91c77c99 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -66,11 +66,6 @@ #define USER_TX_MULTICAST 4 -/** System defines */ -#define MAX_FRAME_SIZE 32 -#define MAX_PAYLOAD_SIZE 128 - - class RF24; /** @@ -122,7 +117,6 @@ struct RF24NetworkHeader */ //RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type&0x7f) {} RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type) {} - /** * Create debugging string * @@ -148,37 +142,50 @@ struct RF24NetworkHeader { RF24NetworkHeader header; /**< Header which is sent with each message */ size_t message_size; /**< The size in bytes of the payload length */ - uint8_t message_buffer[MAX_PAYLOAD_SIZE]; /**< Vector to put the frame payload that will be sent/received over the air */ uint8_t total_fragments; /** Date: Mon, 24 Nov 2014 21:19:23 -0600 Subject: [PATCH 055/105] RPi - Sending multicast fragments - Add support for RPi to send fragmented multicast payloads - Arduino enabled to receive them in last commit --- RPi/RF24Network/RF24Network.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/RPi/RF24Network/RF24Network.cpp b/RPi/RF24Network/RF24Network.cpp index 86f12427..f98698a5 100644 --- a/RPi/RF24Network/RF24Network.cpp +++ b/RPi/RF24Network/RF24Network.cpp @@ -159,7 +159,7 @@ uint8_t RF24Network::update(void) { continue; } - if(header.type>127){ + if(header.type>127 ){ IF_SERIAL_DEBUG_ROUTING(printf_P(PSTR("%lu RT: System payload rcvd %d\n"),millis(),header.type);); if( (header.type < 148 || header.type > 150) && header.type != NETWORK_MORE_FRAGMENTS_NACK){ return header.type; @@ -407,7 +407,7 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ header.from_node = node_address; // Build the full frame to send - memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); + /*memcpy(frame_buffer,&header,sizeof(RF24NetworkHeader)); if (len) memcpy(frame_buffer + sizeof(RF24NetworkHeader),message,std::min(frame_size-sizeof(RF24NetworkHeader),len)); @@ -418,7 +418,9 @@ bool RF24Network::multicast(RF24NetworkHeader& header,const void* message, size_ } return write(levelToAddress(level),USER_TX_MULTICAST); + */ + return write(header, message, len, levelToAddress(level)); } #endif //RF24NetworkMulticast @@ -496,7 +498,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le fragmentHeader.type = NETWORK_MORE_FRAGMENTS; //Set the more fragments flag to indicate a fragmented frame } } - + printf("type %d to %o\n",fragmentHeader.type,fragmentHeader.to_node); size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = std::min(len-offset,max_frame_payload_size); @@ -507,6 +509,8 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //Try to send the payload chunk with the copied header frame_size = sizeof(RF24NetworkHeader)+fragmentLen; bool ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); + + if(writeDirect != 070){ delay(2); printf("delay M\n");} //Delay 5ms between sending multicast payloads //header.id++; uint8_t counter = 0; /*while(!ok){ @@ -573,6 +577,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself if ( header.to_node == node_address ) { // Build the frame to send + RF24NetworkFrame frame = RF24NetworkFrame(header,message,std::min(MAX_FRAME_SIZE-sizeof(RF24NetworkHeader),len)); // Just queue it in the received queue return enqueue(frame); From 1f1a0ce7601e8fe1ef7a8141720a2a67fda49d0f Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 26 Nov 2014 04:10:46 -0600 Subject: [PATCH 056/105] Fix external data disabled without debug minimal - External data was disabled if debug_minimal was turned off - Fix some of the frag debug output --- RF24Network.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index b44f0876..eb155b2e 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -173,8 +173,9 @@ uint8_t RF24Network::update(void) if( enqueue(frame) == 2 ){ //External data received #if defined (SERIAL_DEBUG_MINIMAL) //Serial.println("ret ext"); - return EXTERNAL_DATA_TYPE; #endif + return EXTERNAL_DATA_TYPE; + } }else{ @@ -489,7 +490,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le uint8_t fragment_id = 1 + ((len - 1) / max_frame_payload_size); //the number of fragments to send = ceil(len/max_frame_payload_size) uint8_t msgCount = 0; - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG Total message fragments %i\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG Total message fragments %d\n\r",millis(),fragment_id);); while (fragment_id > 0) { @@ -511,7 +512,7 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le size_t offset = msgCount*max_frame_payload_size; size_t fragmentLen = min(len-offset,max_frame_payload_size); - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG try to transmit fragmented payload of size %i Bytes with fragmentID '%i'\n\r",millis(),fragmentLen,fragment_id);); + // IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG try to transmit fragmented payload of size %d Bytes with fragmentID '%d'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header //printf("frz %d\n",frame_size); @@ -532,13 +533,13 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le //} if (!ok) { - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' failed. Abort.\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG message transmission with fragmentID '%d' failed. Abort.\n\r",millis(),fragment_id);); return false; break; } //Message was successful sent - IF_SERIAL_DEBUG_FRAGMENTATION(printf("%u: FRG message transmission with fragmentID '%i' sucessfull.\n\r",millis(),fragment_id);); + IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG message transmission with fragmentID '%d' sucessfull.\n\r",millis(),fragment_id);); //Check and modify counters fragment_id--; From 18aaa232b47c23e4db440cc865305b6e96ac7563 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 28 Nov 2014 15:10:24 -0600 Subject: [PATCH 057/105] Fixes etc --- RF24Network.cpp | 40 +++++++++++++++++++++++----------------- RF24Network.h | 19 ++++++++++--------- RF24Network_config.h | 2 -- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index eb155b2e..cae1198a 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -150,12 +150,12 @@ uint8_t RF24Network::update(void) write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); delay(50); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - //printf("Fwd add response to 0%o\n",requester); + printf("Fwd add response to 0%o\n",requester); continue; } } if(header.type == NETWORK_REQ_ADDRESS && node_address){ - //printf("Fwd add req to 0\n"); + printf("Fwd add req to 0\n"); header.from_node = node_address; header.to_node = 0; write(header.to_node,TX_NORMAL); @@ -206,7 +206,7 @@ uint8_t RF24Network::update(void) else{ IF_SERIAL_DEBUG_ROUTING( printf_P(PSTR("MAC: Drop duplicate multicast frame %d from 0%o\n"),header.id,header.from_node); ); } - }else{ + }else{ write(header.to_node,1); //Send it on, indicate it is a routed payload } #else @@ -238,9 +238,8 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) if(frame.header.type == NETWORK_FIRST_FRAGMENT){ if(frame.header.reserved *24 > MAX_PAYLOAD_SIZE){ // TUN: - //if(frame.header.reserved *24 > MAX_PAYLOAD_SIZE + 28){ -#if defined (SERIAL_DEBUG_FRAGMENTATION) +#if defined (SERIAL_DEBUG_FRAGMENTATION) || defined (SERIAL_DEBUG_MINIMAL) printf("Frag frame with %d frags exceeds MAX_PAYLOAD_SIZE\n",frame.header.reserved); #endif frag_queue.header.reserved = 0; @@ -265,7 +264,7 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) if(frame.header.type == NETWORK_MORE_FRAGMENTS || frame.header.type == NETWORK_MORE_FRAGMENTS_NACK){ if(frame.header.reserved != frag_queue.header.reserved){ #if defined (SERIAL_DEBUG_FRAGMENTATION) - printf("Dropped out of order frame %d expected %d\n",frame.header.reserved,frame.header.reserved); + printf("1 Dropped out of order frame %d expected %d\n",frame.header.reserved,frag_queue.header.reserved); #endif frag_queue.header.reserved = 0; return 0; @@ -279,7 +278,7 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) if(frag_queue.header.reserved != 1 || frag_queue.header.id != frame.header.id){ #if defined (SERIAL_DEBUG_FRAGMENTATION) - printf("Dropped out of order frame frag %d header id %d expected last (1) \n",frame.header.reserved,frame.header.id); + printf("2 Dropped out of order frame frag %d header id %d expected last (1) \n",frame.header.reserved,frame.header.id); #endif frag_queue.header.reserved = 0; return 0; @@ -340,16 +339,18 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) return 2; } +// memcpy(next_frame,&frame,11); +// memcpy(next_frame+11,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); memcpy(next_frame,&frame,11); memcpy(next_frame+11,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); #if defined (SERIAL_DEBUG_FRAGMENTATION) - for(int i=0; i 0) { - - memcpy(message,&frame_queue[11],frame->message_size); - IF_SERIAL_DEBUG(printf("%lu: NET message size %d\n",millis(),frame->message_size);); + bufsize = frame_queue[8]; + bufsize |= frame_queue[9] << 8; + maxlen = min(maxlen,bufsize); + memcpy(message,frame_queue+11,maxlen); + IF_SERIAL_DEBUG(printf("%lu: NET message size %d\n",millis(),bufsize);); - size_t len = frame->message_size; + size_t len = bufsize; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); } - next_frame-=frame->message_size+11; // - memmove(frame_queue,frame_queue+frame->message_size+11,sizeof(frame_queue)); + bufsize+=11; + memmove(frame_queue,frame_queue+bufsize,sizeof(frame_queue)- bufsize); + while(bufsize--){ + next_frame--; + } //IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); } @@ -734,6 +739,7 @@ bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe, bool multicast ) radio.openWritingPipe(out_pipe); radio.writeFast(&frame_buffer, frame_size,multicast); ok = radio.txStandBy(txTimeout); + #else radio1.openWritingPipe(out_pipe); radio1.writeFast(frame_buffer, frame_size); diff --git a/RF24Network.h b/RF24Network.h index 91c77c99..2ae48de4 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -64,7 +64,7 @@ #define USER_TX_TO_PHYSICAL_ADDRESS 2 //no network ACK #define USER_TX_TO_LOGICAL_ADDRESS 3 // network ACK #define USER_TX_MULTICAST 4 - +#define MAX_FRAME_SIZE 32 //Size of individual radio frames class RF24; @@ -485,9 +485,9 @@ class RF24Network #endif #else #if defined (DISABLE_FRAGMENTATION) - uint8_t frame_queue[3*(MAX_FRAME_SIZE+3)]; /**< Space for a small set of frames that need to be delivered to the app layer */ + uint8_t frame_queue[5*(MAX_FRAME_SIZE+11)]; /**< Space for a small set of frames that need to be delivered to the app layer */ #else - uint8_t frame_queue[MAX_PAYLOAD_SIZE+MAX_FRAME_SIZE+11]; /**< Space for a small set of frames that need to be delivered to the app layer */ + uint8_t frame_queue[5*(MAX_FRAME_SIZE+11)]; /**< Space for a small set of frames that need to be delivered to the app layer */ #endif #endif uint8_t* next_frame; /**< Pointer into the @p frame_queue where we should place the next received frame */ @@ -594,8 +594,11 @@ class RF24Network * * @section Features Features * - * Whats new? - * @note Network Message Types Change: (Oct 8, 2014) Requires re-installation on all nodes
+ * Whats new?
+ * New functionality: (Nov 24) Fragmentation & reassembly supported on both RPi and Arduino
+ * New functionality: (Nov 24) Partial support for fragmented multicast payloads. (Only working with sending from RPi to Arduino)
+ * Note: structure of network frames is changed, these are only used by external applications like RF24Ethernet and RF24toTUN, and for fragmentation
+ * Network Message Types Change: (Oct 8, 2014) Requires re-installation on all nodes
* New functionality: User message types 1 through 64 will not receive a network ack * * The layer provides: @@ -612,10 +615,8 @@ class RF24Network * @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. + * The layer does not provide: + * @li Dynamic address assignment. (See RF24Mesh) * * @section More How to learn more * diff --git a/RF24Network_config.h b/RF24Network_config.h index dbe90af8..cf2892a7 100644 --- a/RF24Network_config.h +++ b/RF24Network_config.h @@ -26,10 +26,8 @@ //#define DISABLE_FRAGMENTATION // Saves a bit of memory space by disabling fragmentation /** System defines */ -#define MAX_FRAME_SIZE 32 //Size of individual radio frames #define MAX_PAYLOAD_SIZE 128 //Size of fragmented network frames Note: With RF24ethernet, assign in multiples of 24. General minimum is 96 (a 32-byte ping from windows is 74 bytes, (Ethernet Header is 42)) - //#define SERIAL_DEBUG //#define SERIAL_DEBUG_MINIMAL //#define SERIAL_DEBUG_ROUTING From 44f559455a374cd22412dc3d67ea6192403a2ea4 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 6 Dec 2014 05:02:36 -0600 Subject: [PATCH 058/105] Fragmentation and cache cleanup - Set default max payload size to 120 - Add #define DISABLE_USER_PAYLOADS to disable the user cache, for use with external systems that have their own cache, like RF24Ethernet - prevents buffering any data that is not an external data type - Remove total_fragments from RF24NetworkFrame - Increase delay for corrupt dynamic payloads - Cleanup fragmentation code a bit --- RF24Network.cpp | 108 ++++++++++++++++--------------------------- RF24Network.h | 32 ++++--------- RF24Network_config.h | 3 +- 3 files changed, 52 insertions(+), 91 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index cae1198a..10412b5e 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -111,9 +111,8 @@ uint8_t RF24Network::update(void) while ( radio.isValid() && radio.available())//&pipe_num) ) { - frame_size = radio.getDynamicPayloadSize(); - if(!frame_size){delay(5);continue;} + if(!frame_size){delay(10);continue;} // Dump the payloads until we've gotten everything // Fetch the payload, and see if this was the last one. @@ -130,9 +129,8 @@ uint8_t RF24Network::update(void) continue; } - RF24NetworkFrame frame = RF24NetworkFrame(header,frame_size-sizeof(RF24NetworkHeader),0); + RF24NetworkFrame frame = RF24NetworkFrame(header,frame_size-sizeof(RF24NetworkHeader)); frame.message_buffer = frame_buffer; - uint8_t res = header.type; // Is this for us? if ( header.to_node == node_address ){ @@ -150,12 +148,12 @@ uint8_t RF24Network::update(void) write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); delay(50); write(header.to_node,USER_TX_TO_PHYSICAL_ADDRESS); - printf("Fwd add response to 0%o\n",requester); + //printf("Fwd add response to 0%o\n",requester); continue; } } if(header.type == NETWORK_REQ_ADDRESS && node_address){ - printf("Fwd add req to 0\n"); + //printf("Fwd add req to 0\n"); header.from_node = node_address; header.to_node = 0; write(header.to_node,TX_NORMAL); @@ -172,7 +170,7 @@ uint8_t RF24Network::update(void) if( enqueue(frame) == 2 ){ //External data received #if defined (SERIAL_DEBUG_MINIMAL) - //Serial.println("ret ext"); + Serial.println("ret ext"); #endif return EXTERNAL_DATA_TYPE; @@ -240,31 +238,30 @@ uint8_t RF24Network::enqueue(RF24NetworkFrame frame) // TUN: //if(frame.header.reserved *24 > MAX_PAYLOAD_SIZE + 28){ #if defined (SERIAL_DEBUG_FRAGMENTATION) || defined (SERIAL_DEBUG_MINIMAL) - printf("Frag frame with %d frags exceeds MAX_PAYLOAD_SIZE\n",frame.header.reserved); + printf(PSTR("Frag frame with %d frags exceeds MAX_PAYLOAD_SIZE\n"),frame.header.reserved); #endif frag_queue.header.reserved = 0; return 0; } #if defined (SERIAL_DEBUG_FRAGMENTATION) - printf("queue first, total frags %d\n",frame.header.reserved); + printf(PSTR("queue first, total frags %d\n"),frame.header.reserved); #endif - memcpy(&frag_queue,&frame,11); + memcpy(&frag_queue,&frame,10); memcpy(frag_queue.message_buffer,frame_buffer+sizeof(RF24NetworkHeader),frame.message_size); #if defined (SERIAL_DEBUG_FRAGMENTATION_L2) for(int i=0; i(frame_queue); return frame.message_size; } @@ -413,23 +408,22 @@ size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen) memcpy(&header,frame_queue,8); + bufsize = frame_queue[8]; + bufsize |= frame_queue[9] << 8; + if (maxlen > 0) - { - bufsize = frame_queue[8]; - bufsize |= frame_queue[9] << 8; + { maxlen = min(maxlen,bufsize); - memcpy(message,frame_queue+11,maxlen); + memcpy(message,frame_queue+10,maxlen); IF_SERIAL_DEBUG(printf("%lu: NET message size %d\n",millis(),bufsize);); size_t len = bufsize; IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET r message "),millis());const uint8_t* charPtr = reinterpret_cast(message);while(len--){ printf("%02x ",charPtr[len]);} printf_P(PSTR("\n\r") ) ); } - bufsize+=11; + bufsize+=10; memmove(frame_queue,frame_queue+bufsize,sizeof(frame_queue)- bufsize); - while(bufsize--){ - next_frame--; - } + next_frame-=bufsize; //IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Received %s\n\r"),millis(),header.toString())); } @@ -520,22 +514,8 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le // IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG try to transmit fragmented payload of size %d Bytes with fragmentID '%d'\n\r",millis(),fragmentLen,fragment_id);); //Try to send the payload chunk with the copied header - //printf("frz %d\n",frame_size); frame_size = sizeof(RF24NetworkHeader)+fragmentLen; bool ok = _write(*fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - - //fragmentHeader->id++; - - uint8_t counter = 0; - /*while(!ok){ - fragmentHeader.id++; - ok = _write(fragmentHeader,((char *)message)+offset,fragmentLen,writeDirect); - counter++; - if(counter >= 3){ break; } - }*/ - //if (!noListen) { - // delayMicroseconds(50); - //} if (!ok) { IF_SERIAL_DEBUG_FRAGMENTATION(printf("%lu: FRG message transmission with fragmentID '%d' failed. Abort.\n\r",millis(),fragment_id);); @@ -551,16 +531,10 @@ bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t le msgCount++; } - //noListen = 0; - - // Now, continue listening - //radio.startListening(); - int frag_delay = uint8_t(len/48); delay( min(frag_delay,20)); //Return true if all the chunks where sent successfully - //else return false //IF_SERIAL_DEBUG(printf("%u: NET total message fragments sent %i. txSuccess ",millis(),msgCount); printf("%s\n\r", txSuccess ? "YES" : "NO");); return true; @@ -588,7 +562,7 @@ bool RF24Network::_write(RF24NetworkHeader& header,const void* message, size_t l // If the user is trying to send it to himself if ( header.to_node == node_address ){ - RF24NetworkFrame frame = RF24NetworkFrame(header,len,0); + RF24NetworkFrame frame = RF24NetworkFrame(header,len); // Just queue it in the received queue return enqueue(frame); } diff --git a/RF24Network.h b/RF24Network.h index 2ae48de4..5b838e25 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -142,7 +142,7 @@ struct RF24NetworkHeader { RF24NetworkHeader header; /**< Header which is sent with each message */ size_t message_size; /**< The size in bytes of the payload length */ - uint8_t total_fragments; /** Date: Mon, 8 Dec 2014 05:48:22 -0600 Subject: [PATCH 059/105] Rough Arduino/RPi code merge Merge Arduino and RPi code into the same files to simplify development and maintenance --- Doxyfile | 2492 +++++++++++------ RPi/RF24Network/Makefile => Makefile | 0 README.md | 107 + RF24Network.cpp | 390 ++- RF24Network.h | 102 +- RF24Network_config.h | 49 +- RPi/RF24Network/Doxyfile | 1551 ---------- RPi/RF24Network/Jamfile | 3 - RPi/RF24Network/README.md | 111 +- RPi/RF24Network/RF24Network.cpp | 946 ------- RPi/RF24Network/RF24Network.h | 440 --- RPi/RF24Network/RF24Network_config.h | 85 - RPi/RF24Network/Sync.cpp | 93 - RPi/RF24Network/Sync.h | 85 - RPi/RF24Network/bcm2835.c | 1451 ---------- RPi/RF24Network/bcm2835.h | 1353 --------- RPi/readme.md | 111 +- .../examples => examples_RPi}/Makefile | 0 .../helloworld_rx.cpp | 0 .../helloworld_tx.cpp | 0 .../examples => examples_RPi}/rx-test.cpp | 0 .../examples => examples_RPi}/temperature.txt | 0 22 files changed, 2161 insertions(+), 7208 deletions(-) rename RPi/RF24Network/Makefile => Makefile (100%) delete mode 100644 RPi/RF24Network/Doxyfile delete mode 100644 RPi/RF24Network/Jamfile delete mode 100644 RPi/RF24Network/RF24Network.cpp delete mode 100644 RPi/RF24Network/RF24Network.h delete mode 100644 RPi/RF24Network/RF24Network_config.h delete mode 100644 RPi/RF24Network/Sync.cpp delete mode 100644 RPi/RF24Network/Sync.h delete mode 100644 RPi/RF24Network/bcm2835.c delete mode 100644 RPi/RF24Network/bcm2835.h rename {RPi/RF24Network/examples => examples_RPi}/Makefile (100%) rename {RPi/RF24Network/examples => examples_RPi}/helloworld_rx.cpp (100%) rename {RPi/RF24Network/examples => examples_RPi}/helloworld_tx.cpp (100%) rename {RPi/RF24Network/examples => examples_RPi}/rx-test.cpp (100%) rename {RPi/RF24Network/examples => examples_RPi}/temperature.txt (100%) diff --git a/Doxyfile b/Doxyfile index 0ed18674..bcf09a54 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,96 +1,121 @@ -# Doxyfile 1.6.3 +# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Newly Optimized RF24Network Layer" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. -PROJECT_NAME = RF24Network +PROJECT_BRIEF = "2014 - Optimized RF24 Network Layer for NRF24L01 radios" -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. -PROJECT_NUMBER = v1 +PROJECT_LOGO = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. -OUTPUT_DIRECTORY = docs +OUTPUT_DIRECTORY = "../../../ArduinoBuilds/RF24Network Docs" -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO @@ -98,152 +123,204 @@ ALWAYS_DETAILED_SEC = NO # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = YES -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = -ALIASES = +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = -EXTENSION_MAPPING = +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = YES @@ -251,1108 +328,1698 @@ IDL_PROPERTY_SUPPORT = YES # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. -TYPEDEF_HIDES_STRUCT = NO +TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. -SYMBOL_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. +# The default value is: system dependent. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. -SORT_BY_SCOPE_NAME = NO +SORT_BY_SCOPE_NAME = YES -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. -GENERATE_TODOLIST = YES +STRICT_PROTO_MATCHING = NO -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. -SHOW_USED_FILES = YES +SHOW_USED_FILES = NO -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. -SHOW_DIRECTORIES = NO +SHOW_FILES = NO -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. -FILE_VERSION_FILTER = +LAYOUT_FILE = -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. -LAYOUT_FILE = +CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. -INPUT = . +INPUT = . # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. FILE_PATTERNS = *.h -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. RECURSIVE = NO -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. -EXCLUDE = +EXCLUDE = -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = bcm2835* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). -EXAMPLE_PATH = examples +EXAMPLE_PATH = examples \ + RPi/RF24Network/examples # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = YES -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. -REFERENCES_LINK_SOURCE = NO +USE_HTAGS = NO -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. -USE_HTAGS = NO +VERBATIM_HEADERS = YES -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. -VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. -ALPHABETICAL_INDEX = NO +ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -COLS_IN_ALPHA_INDEX = 5 +COLS_IN_ALPHA_INDEX = 10 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a # standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = ../RF24/doxygen-custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. -USE_INLINE_TREES = NO +ENUM_VALUES_PER_LINE = 4 -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /