From c4fb0dc2cbc105c0fbf46f9af39fa9972f2f2a6c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 12 Jun 2011 22:13:15 -0700 Subject: [PATCH] Major conversion to octal addressing --- RF24Network.cpp | 223 ++++++++++++++++++++++++++----- RF24Network.h | 40 ++---- examples/meshping/Jamfile | 2 +- examples/meshping/meshping.pde | 34 +---- examples/meshping/nodeconfig.cpp | 4 +- 5 files changed, 208 insertions(+), 95 deletions(-) diff --git a/RF24Network.cpp b/RF24Network.cpp index 4909b19b..0218b561 100644 --- a/RF24Network.cpp +++ b/RF24Network.cpp @@ -11,7 +11,7 @@ #include #include -#undef SERIAL_DEBUG +#define SERIAL_DEBUG #ifdef SERIAL_DEBUG #define IF_SERIAL_DEBUG(x) (x) #else @@ -26,33 +26,35 @@ uint16_t RF24NetworkHeader::next_id = 1; +uint64_t pipe_address( uint16_t node, uint8_t pipe ); + /******************************************************************/ -RF24Network::RF24Network( RF24& _radio, const RF24NodeLine* _topology ): radio(_radio), topology(_topology), next_frame(frame_queue), bidirectional(true) +RF24Network::RF24Network( RF24& _radio ): radio(_radio), next_frame(frame_queue), bidirectional(true) { - // Find out how many nodes are defined - num_nodes = 0; - const RF24NodeLine* i = topology; - while ( (i++)->parent_node != 0xFFFF ) - ++num_nodes; } /******************************************************************/ void RF24Network::begin(uint8_t _channel, uint16_t _node_address, rf24_direction_e _direction ) { - if ( _node_address < num_nodes ) - node_address = _node_address; + node_address = _node_address; if ( _direction == RF24_NET_UNIDIRECTIONAL ) bidirectional = false; + // Set up the radio the way we want it to look radio.setChannel(_channel); radio.setDataRate(RF24_1MBPS); radio.setCRCLength(RF24_CRC_16); + + // Setup our address helper cache + setup_address(); - open_pipes(); + // Open up our listening pipe + radio.openReadingPipe(0,pipe_address(_node_address,0)); + // Spew debugging state about the radio radio.printDetails(); } @@ -88,6 +90,16 @@ void RF24Network::update(void) else // Relay it write(header.to_node); + + // 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)); + } } } } @@ -186,34 +198,58 @@ bool RF24Network::write(uint16_t to_node) // First, stop listening so we can talk. radio.stopListening(); - // Which pipe should we use to get the message to the "to_node"? - // We need to find a node who is OUR CHILD that either IS the to_node - // or has the to_node as one of ITS children. Failing that, we'll just - // send it back to the parent to deal with. - uint8_t out_node = find_node(node_address,to_node); + // 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 the node is a direct child, + if ( is_direct_child(to_node) ) + { + // Send directly + send_node = to_node; - // If we get '0' as a node, there is a problem - if ( ! out_node ) + // To its listening pipe + send_pipe = 0; + } + // 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) ) { - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: NET Cannot send to node %u, discarded\n\r"),millis(),to_node)); - return ok; + send_node = direct_child_route_to(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)); // First, stop listening so we can talk radio.stopListening(); - // Determine which pipe we should be sending this on - uint64_t out_pipe; + // Put the frame on the pipe + ok = write_to_pipe( send_node, send_pipe ); + + // 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. - // In Bidirectional mode, if this node is our child, we talk on it's listening pipe. - if ( bidirectional && topology[out_node].parent_node == node_address ) - out_pipe = topology[out_node].listening_pipe; + if ( !ok && send_node == parent_node ) + ok = write_to_pipe( parent_node, 0 ); - // Otherwise, it's our parent so we talk on OUR talking pipe - // (In uni-directional mode, we can ONLY talk to our parent) - else - out_pipe = topology[node_address].talking_pipe; + // Now, continue listening + radio.startListening(); + + return ok; +} + +/******************************************************************/ + +bool RF24Network::write_to_pipe( uint16_t node, uint8_t pipe ) +{ + bool ok = false; + uint64_t out_pipe = pipe_address( node, pipe ); + // Open the correct pipe for writing. radio.openWritingPipe(out_pipe); @@ -225,16 +261,13 @@ bool RF24Network::write(uint16_t to_node) } while ( !ok && --attempts ); - // Now, continue listening - radio.startListening(); - - IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %x %s\n\r"),millis(),(uint16_t)out_pipe,ok?"ok":"failed")); + IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %lx %s\n\r"),millis(),(uint32_t)out_pipe,ok?"ok":"failed")); return ok; } /******************************************************************/ - +#if 0 void RF24Network::open_pipes(void) { // In order to open the right pipes, we need to know whether the node has parents @@ -283,7 +316,6 @@ void RF24Network::open_pipes(void) //if ( bidirectional || has_children ) radio.startListening(); } - /******************************************************************/ /** @@ -320,7 +352,7 @@ uint16_t RF24Network::find_node( uint16_t current_node, uint16_t target_node ) } return out_node; } - +#endif /******************************************************************/ const char* RF24NetworkHeader::toString(void) const @@ -330,4 +362,125 @@ const char* RF24NetworkHeader::toString(void) const return buffer; } +/******************************************************************/ + +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 + // one higher number. + // + // e.g. node 0234 is a direct child of 034, and node 01234 is a + // descendant but not a direct child + + // First, is it even a descendant? + if ( is_descendant(node) ) + { + // Does it only have ONE more level than us? + uint16_t child_node_mask = ( ~ node_mask ) << 3; + result = ( node & child_node_mask ) == 0 ; + } + + return result; +} + +/******************************************************************/ + +bool RF24Network::is_descendant( uint16_t node ) +{ + return ( node & node_mask ) == node_address; +} + +/******************************************************************/ + +void RF24Network::setup_address(void) +{ + // First, establish the node_mask + 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 + uint16_t parent_mask = node_mask >> 3; + + // parent node is the part IN the mask + parent_node = node_address & parent_mask; + + // parent pipe is the part OUT of the mask + uint16_t i = node_address; + uint16_t m = parent_mask; + while (m) + { + i >>= 3; + m >>= 3; + } + parent_pipe = i; + +#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 +} + +/******************************************************************/ + +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 ) +{ + uint16_t i = node; + uint16_t m = node_mask; + + while (m) + { + i >>= 3; + m >>= 3; + } + + return i & 0B111; +} + +/******************************************************************/ + +uint64_t pipe_address( uint16_t node, uint8_t pipe ) +{ + static uint8_t pipe_segment[] = { 0x3c, 0x5a, 0x69, 0x96, 0xa5, 0xc3 }; + + uint64_t result; + 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; + } + +#ifdef SERIAL_DEBUG + + uint32_t* top = reinterpret_cast(out+1); + printf_P(PSTR("pipe_address(%x,%u)=%lx%x\n\r"),node,pipe,*top,*out); + +#endif + + return result; +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24Network.h b/RF24Network.h index 5e9f6a04..9d897d73 100644 --- a/RF24Network.h +++ b/RF24Network.h @@ -42,16 +42,6 @@ struct RF24NodeLine uint16_t parent_node; /**< Logical address of our parent node */ }; -/** - * Marker for the start of a topology list - */ -#define RF24NODELINE_LIST_BEGIN { 0xFFFFFFFFFFLL, 0xFFFFFFFFFFLL, 0 }, - -/** - * Marker for the end of a topology list - */ -#define RF24NODELINE_LIST_END { 0xFFFFFFFFFFLL, 0xFFFFFFFFFFLL, -1 } - /** * Header which is sent with each message * @@ -120,27 +110,10 @@ class RF24Network /** * Construct the network * - * This requires a static topology. Send in @p _topology as a pointer to a - * terminated array of RF24NodeLines, one node line for each valid node address. - * Adding a new node to the network requires adding/changing the entry in this - * table and re-flashing the entire network. Yes it would be nice to manage - * this dynamically! Someday. - * - * @code - * RF24NodeLine topology[] = - * { - * RF24NODELINE_LIST_BEGIN - * { 0xE7E7E7E7F1LL, 0xE7E7E7E701LL, 0 }, // Node 1: Base, has no parent - * { 0xE7E7E7E7FELL, 0xE7E7E7E70ELL, 1 }, // Node 2: Leaf, child of #1 - * RF24NODELINE_LIST_END - * }; - * @endcode - * * @param _radio The underlying radio driver instance - * @param _topology Terminated array of node addresses / pipe mappings. * */ - RF24Network( RF24& _radio, const RF24NodeLine* _topology); + RF24Network( RF24& _radio ); /** * Bring up the network @@ -203,8 +176,15 @@ class RF24Network 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 enqueue(void); + 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); + private: RF24& radio; /**< Underlying radio driver, provides link/physical layers */ uint16_t node_address; /**< Logical node address of this unit, 1 .. UINT_MAX */ @@ -215,6 +195,10 @@ class RF24Network 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 */ bool bidirectional; /**< Whether we are in bi-dir (true) or uni-dir (false) mode */ + + 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 */ }; /** diff --git a/examples/meshping/Jamfile b/examples/meshping/Jamfile index 5b038d19..ee88625c 100644 --- a/examples/meshping/Jamfile +++ b/examples/meshping/Jamfile @@ -39,7 +39,7 @@ AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) ; CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; CFLAGS = -Os -Wall -mmcu=$(MCU) $(CTUNING) ; CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; diff --git a/examples/meshping/meshping.pde b/examples/meshping/meshping.pde index 67bb6b22..f15cc2f3 100644 --- a/examples/meshping/meshping.pde +++ b/examples/meshping/meshping.pde @@ -41,24 +41,13 @@ #undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) -// This is a very simple example, using 2 nodes. This exact same code will work -// With an unlimited number of nodes connected in a giant mesh. Increase the ping -// interval with many more nodes! -RF24NodeLine topology[] = -{ - RF24NODELINE_LIST_BEGIN - { 0xE7E7E7E7F1LL, 0xE7E7E7E701LL, 0 }, // Node 1: Base, has no parent - { 0xE7E7E7E7F8LL, 0xE7E7E7E708LL, 1 }, // Node 2: Leaf, child of #1 - RF24NODELINE_LIST_END -}; - RF24 radio(8,9); -RF24Network network(radio,topology); +RF24Network network(radio); // Our node address uint16_t this_node; uint16_t next_node_to; -const uint16_t max_node = sizeof(topology) / sizeof(RF24NodeLine) - 2 ; +const uint16_t max_node = 2; // The message that we send is just a ulong, containing the time unsigned long message; @@ -66,7 +55,6 @@ unsigned long message; // Delay manager to send pings regularly const unsigned long interval = 2000; // ms unsigned long last_time_sent; -bool running = false; void setup(void) { @@ -88,15 +76,7 @@ void setup(void) // Decide which node to send to next next_node_to = this_node + 1; if ( next_node_to > max_node ) - next_node_to = 1; - - // Node 1 starts running right away, the rest wait - // This makes it easier to test because we can set up - // a repeatable system. Bring all the other nodes up - // first, and then bring up node 1, and it will all - // work the same every time. - if ( this_node == 1 ) - running = true; + next_node_to = 0; // // Bring up the RF network @@ -119,15 +99,11 @@ void loop(void) RF24NetworkHeader header; network.read(header,&message,sizeof(unsigned long)); printf_P(PSTR("%lu: APP Received %lu from %u\n\r"),millis(),message,header.from_node); - - // We can start running once we get our first message - if ( !running ) - running = true; } // Send a ping to the other guy every 'interval' ms unsigned long now = millis(); - if ( running && now - last_time_sent >= interval ) + if ( now - last_time_sent >= interval ) { last_time_sent = now; @@ -151,7 +127,7 @@ void loop(void) // Decide which node to send to next if ( ++next_node_to > max_node ) - next_node_to = 1; + next_node_to = 0; } // Listen for a new node address diff --git a/examples/meshping/nodeconfig.cpp b/examples/meshping/nodeconfig.cpp index 594ecd77..4ef0dbeb 100644 --- a/examples/meshping/nodeconfig.cpp +++ b/examples/meshping/nodeconfig.cpp @@ -36,7 +36,7 @@ uint8_t nodeconfig_read(void) } else { - printf_P(PSTR("*** No valid address found. Send 1-9 via serial to set node address\n\r")); + printf_P(PSTR("*** No valid address found. Send 0-9 via serial to set node address\n\r")); } return result; @@ -51,7 +51,7 @@ void nodeconfig_listen(void) { // If the character on serial input is in a valid range... char c = Serial.read(); - if ( c >= '1' && c <= '9' ) + if ( c >= '0' && c <= '9' ) { // It is our address eeprom_write_byte(address_at_eeprom_location,valid_eeprom_flag);