From 6e25453ffabf583c0304b701a3cfcf8f98c9fc94 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 29 Jun 2014 12:02:44 -0600 Subject: [PATCH] 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