Ported Arduino RF24Network library.

Files at this revision

API Documentation at this revision

Comitter:
zcw607
Date:
Mon Mar 09 20:49:13 2015 +0000
Child:
1:b5836d9f8de5
Commit message:
ported Arduino RF24Network

Changed in this revision

RF24Network.cpp Show annotated file Show diff for this revision Revisions of this file
RF24Network.h Show annotated file Show diff for this revision Revisions of this file
RF24Network_config.h Show annotated file Show diff for this revision Revisions of this file
Sync.cpp Show annotated file Show diff for this revision Revisions of this file
Sync.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RF24Network.cpp	Mon Mar 09 20:49:13 2015 +0000
@@ -0,0 +1,429 @@
+/*
+ Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+#include "RF24Network_config.h"
+#include "RF24.h"
+#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 );
+
+/******************************************************************/
+
+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) )
+    return;
+
+  node_address = _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);
+
+  // Setup our address helper cache
+  setup_address();
+  
+  // Open up all listening pipes
+  int i = 6;
+  while (i--)
+    radio.openReadingPipe(i,pipe_address(_node_address,i));
+  radio.startListening();
+
+  // Spew debugging state about the radio
+  radio.printDetails();
+}
+
+/******************************************************************/
+
+void RF24Network::update(void)
+{
+  // if there is data ready
+  uint8_t pipe_num;
+  while ( radio.available(&pipe_num) )
+  {
+    // Dump the payloads until we've gotten everything
+    boolean done = false;
+    while (!done)
+    {
+      // Fetch the payload, and see if this was the last one.
+      done = radio.read( frame_buffer, sizeof(frame_buffer) );
+
+      // Read the beginning of the frame as the header
+      const RF24NetworkHeader& header = * reinterpret_cast<RF24NetworkHeader*>(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<const uint16_t*>(frame_buffer + sizeof(RF24NetworkHeader));printf_P(PSTR("%lu: 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;
+
+      // Is this for us?
+      if ( header.to_node == node_address )
+    // Add it to the buffer of frames for us
+    enqueue();
+      else
+    // Relay it
+    write(header.to_node);
+
+      // 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
+    }
+  }
+}
+
+/******************************************************************/
+
+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
+  if ( next_frame < frame_queue + sizeof(frame_queue) )
+  {
+    memcpy(next_frame,frame_buffer, frame_size );
+    next_frame += frame_size; 
+
+    result = true;
+    IF_SERIAL_DEBUG(printf_P(PSTR("ok\n\r")));
+  }
+  else
+  {
+    IF_SERIAL_DEBUG(printf_P(PSTR("failed\n\r")));
+  }
+
+  return result;
+}
+
+/******************************************************************/
+
+bool RF24Network::available(void)
+{
+  // Are there frames on the queue for us?
+  return (next_frame > frame_queue);
+}
+
+/******************************************************************/
+
+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));
+  }
+}
+
+/******************************************************************/
+
+size_t RF24Network::read(RF24NetworkHeader& header,void* message, size_t maxlen)
+{
+  size_t bufsize = 0;
+
+  if ( available() )
+  {
+    // 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 = 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()));
+  }
+
+  return bufsize;
+}
+
+/******************************************************************/
+
+bool RF24Network::write(RF24NetworkHeader& header,const void* message, size_t len)
+{
+  // Fill out the header
+  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<const uint16_t*>(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
+    return enqueue();
+  else
+    // Otherwise send it out over the air
+    return write(header.to_node);
+}
+
+/******************************************************************/
+
+bool RF24Network::write(uint16_t to_node)
+{
+  bool ok = false;
+  
+  // 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 the node is a direct child,
+  if ( is_direct_child(to_node) )
+  {
+    // Send directly
+    send_node = to_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) )
+  {
+    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();
+
+  // 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.
+#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
+
+  // 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);
+
+  // Retry a few times
+  short attempts = 5;
+  do
+  {
+    ok = radio.write( frame_buffer, frame_size );
+  }
+  while ( !ok && --attempts );
+
+  IF_SERIAL_DEBUG(printf_P(PSTR("%lu: MAC Sent on %lx %S\n\r"),millis(),(uint32_t)out_pipe,ok?PSTR("ok"):PSTR("failed")));
+
+  return ok;
+}
+
+/******************************************************************/
+
+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);
+  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;
+}
+
+/******************************************************************/
+
+bool is_valid_address( uint16_t node )
+{
+  bool result = true;
+
+  while(node)
+  {
+    uint8_t digit = node & 0B111;
+    if (digit < 1 || digit > 5)
+    {
+      result = false;
+      printf_P(PSTR("*** WARNING *** Invalid address 0%o\n\r"),node);
+      break;
+    }
+    node >>= 3;
+  }
+
+  return result;
+}
+
+/******************************************************************/
+
+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<uint8_t*>(&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;
+  }
+
+  IF_SERIAL_DEBUG(uint32_t* top = reinterpret_cast<uint32_t*>(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;
+}
+
+// vim:ai:cin:sts=2 sw=2 ft=cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RF24Network.h	Mon Mar 09 20:49:13 2015 +0000
@@ -0,0 +1,343 @@
+/*
+ Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef __RF24NETWORK_H__
+#define __RF24NETWORK_H__
+
+/**
+ * @file RF24Network.h
+ *
+ * Class declaration for RF24Network
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+class RF24;
+
+/**
+ * Header which is sent with each message
+ *
+ * The frame put over the air consists of this header and a message
+ */
+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 reserved; /**< Reserved for future use */
+
+  static uint16_t next_id; /**< The message ID of the next message to be sent */
+
+  /**
+   * Default constructor
+   *
+   * Simply constructs a blank header
+   */
+  RF24NetworkHeader() {}
+
+  /**
+   * Send constructor
+   *
+   * Use this constructor to create a header and then send a message
+   *
+   * @code
+   *  RF24NetworkHeader header(recipient_address,'t');
+   *  network.write(header,&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.
+   */
+  RF24NetworkHeader(uint16_t _to, unsigned char _type = 0): to_node(_to), id(next_id++), type(_type&0x7f) {}
+
+  /**
+   * 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;
+};
+
+/**
+ * Network Layer for RF24 Radios
+ *
+ * This class implements an OSI Network Layer using nRF24L01(+) radios driven
+ * by RF24 library.
+ */
+
+class RF24Network
+{
+public:
+  /**
+   * Construct the network
+   *
+   * @param _radio The underlying radio driver instance
+   *
+   */
+  RF24Network( RF24& _radio );
+
+  /**
+   * Bring up the network
+   *
+   * @warning Be sure to 'begin' the radio first.
+   *
+   * @param _channel The RF channel to operate on
+   * @param _node_address The logical address of this node
+   */
+  void begin(uint8_t _channel, uint16_t _node_address );
+  
+  /**
+   * Main layer loop
+   *
+   * This function must be called regularly to keep the layer going.  This is where all
+   * the action happens!
+   */
+  void update(void);
+
+  /**
+   * 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
+   *
+   * Reads the next available header without advancing to the next
+   * incoming message.  Useful for doing a switch on the message type
+   *
+   * If there is no message available, the header is not touched
+   *
+   * @param[out] header The header (envelope) of the next message
+   */
+  void peek(RF24NetworkHeader& header);
+
+  /**
+   * Read a message
+   *
+   * @param[out] header The header (envelope) of this message
+   * @param[out] message Pointer to memory where the message should be placed
+   * @param maxlen The largest message size which can be held in @p message
+   * @return The total number of bytes copied into @p message
+   */
+  size_t read(RF24NetworkHeader& header, void* message, size_t maxlen);
+  
+  /**
+   * Send a message
+   *
+   * @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 
+   */
+  bool write(RF24NetworkHeader& header,const void* message, size_t len);
+
+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 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 */
+  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* 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 */
+};
+
+/**
+ * @example helloworld_tx.pde
+ *
+ * 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.
+ */
+
+/**
+ * @example helloworld_rx.pde
+ *
+ * Simplest possible example of using RF24Network.  Put this sketch
+ * on one node, and helloworld_tx.pde on the other.  Tx will send
+ * Rx a nice message every 2 seconds which rx will print out for us.
+ */
+
+/**
+ * @example meshping.pde
+ *
+ * Example of pinging across a mesh network
+ * Using this sketch, each node will send a ping to the base every
+ * few seconds.  The RF24Network library will route the message across
+ * the mesh to the correct node.
+ */
+
+/**
+ * @example sensornet.pde
+ *
+ * Example of a sensor network.
+ * This sketch demonstrates how to use the RF24Network library to
+ * manage a set of low-power sensor nodes which mostly sleep but
+ * awake regularly to send readings to the base.
+ */
+/**
+ * @mainpage Network Layer for RF24 Radios
+ *
+ * This class implements an <a href="http://en.wikipedia.org/wiki/Network_layer">OSI Network Layer</a> using nRF24L01(+) radios driven
+ * by the <a href="http://maniacbug.github.com/RF24/">RF24</a> library.
+ *
+ * @section Purpose Purpose/Goal
+ *
+ * Create an alternative to ZigBee radios for Arduino communication.
+ *
+ * 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 
+ * $6 from many sources.  With the RF24Network layer, I hope to cover many
+ * common communication scenarios.
+ *
+ * Please see the @ref Zigbee page for a comparison against the ZigBee protocols
+ *
+ * @section Features Features
+ *
+ * The layer provides:
+ * @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.
+ * @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 Power-efficient listening.  It would be useful for nodes who are listening
+ * to sleep for extended periods of time if they could know that they would miss
+ * no traffic.
+ * @li Dynamic address assignment.
+ *
+ * @section More How to learn more
+ *
+ * @li <a href="http://maniacbug.github.com/RF24/">RF24: Underlying radio driver</a>
+ * @li <a href="classRF24Network.html">RF24Network Class Documentation</a>
+ * @li <a href="https://github.com/maniacbug/RF24Network/">Source Code</a>
+ * @li <a href="https://github.com/maniacbug/RF24Network/archives/master">Downloads Page</a>
+ * @li <a href="examples.html">Examples Page</a>.  Start with <a href="helloworld_rx_8pde-example.html">helloworld_rx</a> and <a href="helloworld_tx_8pde-example.html">helloworld_tx</a>.
+ *
+ * @section Topology Topology for Mesh Networks using nRF24L01(+)
+ *
+ * This network layer takes advantage of the fundamental capability of the nRF24L01(+) radio to
+ * listen actively to up to 6 other radios at once.  The network is arranged in a 
+ * <a href="http://en.wikipedia.org/wiki/Network_Topology#Tree">Tree Topology</a>, where
+ * one node is the base, and all other nodes are children either of that node, or of another.
+ * Unlike a true mesh network, multiple nodes are not connected together, so there is only one
+ * path to any given node.
+ *
+ * @section Octal Octal Addressing
+ *
+ * Each node must be assigned an 15-bit address by the administrator.  This address exactly
+ * describes the position of the node within the tree.  The address is an octal number.  Each
+ * digit in the address represents a position in the tree further from the base.
+ *
+ * @li Node 00 is the base node.
+ * @li Nodes 01-05 are nodes whose parent is the base.
+ * @li Node 021 is the second child of node 01.
+ * @li Node 0321 is the third child of node 021, an so on.
+ * @li The largest node address is 05555, so 3,125 nodes are allowed on a single channel.
+ *
+ * @section Routing How routing is handled
+ *
+ * When sending a message using RF24Network::write(), you fill in the header with the logical
+ * node address.  The network layer figures out the right path to find that node, and sends
+ * it through the system until it gets to the right place.  This works even if the two nodes
+ * are far separated, as it will send the message down to the base node, and then back out
+ * to the final destination.
+ *
+ * All of this work is handled by the RF24Network::update() method, so be sure to call it
+ * regularly or your network will miss packets.
+ *
+ * @section Startup Starting up a node
+ *
+ * When a node starts up, it only has to contact its parent to establish communication.
+ * No direct connection to the Base node is needed.  This is useful in situations where
+ * relay nodes are being used to bridge the distance to the base, so leaf nodes are out
+ * of range of the base.
+ *
+ * @section Directionality Directionality 
+ *
+ * By default all nodes are always listening, so messages will quickly reach
+ * their destination.  
+ * 
+ * You may choose to sleep any nodes which do not have any active children on the network
+ * (i.e. leaf nodes).  This is useful in a case where
+ * the leaf nodes are operating on batteries and need to sleep.
+ * This is useful for a sensor network.  The leaf nodes can sleep most of the time, and wake
+ * every few minutes to send in a reading.  However, messages cannot be sent to these 
+ * sleeping nodes.
+ *
+ * In the future, I plan to write a system where messages can still be passed upward from
+ * the base, and get delivered when a sleeping node is ready to receive them.  The radio
+ * and underlying driver support 'ack payloads', which will be a handy mechanism for this.
+ *
+ * @page Zigbee Comparison to ZigBee
+ *
+ * This network layer is influenced by the design of ZigBee, but does not implement it
+ * directly.  
+ *
+ * @section Advantage Which is better?
+ *
+ * ZigBee is a much more robust, feature-rich set of protocols, with many different vendors
+ * providing compatible chips.
+ *
+ * RF24Network is cheap.  While ZigBee radios are well over $20, nRF24L01 modules can be found
+ * for under $6.  My personal favorite is 
+ * <a href="http://www.mdfly.com/index.php?main_page=product_info&products_id=82">MDFly RF-IS2401</a>.
+ *
+ * @section Contrast Similiarities & Differences
+ *
+ * Here are some comparisons between RF24Network and ZigBee.
+ *
+ * @li Both networks support Star and Tree topologies.  Only Zigbee supports a true mesh.
+ * @li In both networks, only leaf nodes can sleep (see @ref NodeNames).
+ * @li ZigBee nodes are configured using AT commands, or a separate Windows application. 
+ * RF24 nodes are configured by recompiliing the firmware or writing to EEPROM.
+ *
+ * @section NodeNames Node Naming
+ *
+ * @li Leaf node: A node at the outer edge of the network with no children.  ZigBee calls it
+ * an End Device node.
+ * @li Relay node: A node which has both parents and children, and relays messages from one
+ * to the other.  ZigBee calls it a Router.
+ * @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.
+ */
+
+#endif // __RF24NETWORK_H__
+// vim:ai:cin:sts=2 sw=2 ft=cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RF24Network_config.h	Mon Mar 09 20:49:13 2015 +0000
@@ -0,0 +1,23 @@
+
+/*
+ Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef __RF24_CONFIG_H__
+#define __RF24_CONFIG_H__
+
+#include "mbed.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#define _BV(x) (1<<(x))
+
+
+#endif // __RF24_CONFIG_H__
+// vim:ai:cin:sts=2 sw=2 ft=cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sync.cpp	Mon Mar 09 20:49:13 2015 +0000
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+// STL headers
+// C headers
+#include <stdlib.h>
+// Framework headers
+// Library headers
+#include <RF24Network.h>
+// Project headers
+// This component's header
+#include <Sync.h>
+
+/****************************************************************************/
+
+void Sync::update(void)
+{
+  // Pump the network
+  network.update();
+
+  // Look for changes to the data
+  uint8_t message[32];
+  uint8_t *mptr = message;
+  unsigned at = 0;
+  while ( at < len )
+  {
+    if ( app_data && internal_data && app_data[at] != internal_data[at] )
+    {
+      // Compose a message with the deltas
+      *mptr++ = at + 1;
+      *mptr++ = app_data[at];
+
+      // Update our internal view
+      internal_data[at] = app_data[at];
+    }
+    ++at;
+  }
+  // Zero out the remainder
+  while ( at++ < sizeof(message) )
+    *mptr++ = 0;
+
+  // If changes, send a message
+  if ( *message )
+  {
+    // TODO handle the case where this has to be broken into
+    // multiple messages
+    RF24NetworkHeader header(/*to node*/ to_node, /*type*/ 'S' /*Sync*/);
+    network.write(header,message,sizeof(message));
+  }
+
+  // Look for messages from the network
+  // Is there anything ready for us?
+  if ( network.available() )
+  {
+    // If so, take a look at it
+    RF24NetworkHeader header;
+    network.peek(header);
+
+    switch (header.type)
+    {
+    case 'S':
+      IF_SERIAL_DEBUG(printf_P(PSTR("%lu: SYN Received sync message\n\r"),millis()));
+
+      network.read(header,&message,sizeof(message));
+      // Parse the message and update the vars
+      mptr = message;
+      at = 0;
+      while ( mptr < message + sizeof(message) )
+      {
+        // A '0' in the first position means we are done
+        if ( !*mptr )
+          break;
+        uint8_t pos = (*mptr++) - 1;
+        uint8_t val = *mptr++;
+
+        IF_SERIAL_DEBUG(printf_P(PSTR("%lu: SYN Updated position %u to value %u\n\r"),millis(),pos,val));
+
+        app_data[pos] = val;
+        internal_data[pos] = val;
+      }
+      break;
+    default:
+      // Leave other messages for the app
+      break;
+    };
+  }
+}
+// vim:cin:ai:sts=2 sw=2 ft=cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sync.h	Mon Mar 09 20:49:13 2015 +0000
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef __SYNC_H__
+#define __SYNC_H__
+
+// STL headers
+// C headers
+#include <stdlib.h>
+#include <string.h>
+// Framework headers
+// Library headers
+#include <RF24Network_config.h>
+// Project headers
+
+class RF24Network;
+
+/**
+ * Synchronizes a shared set of variables between multiple nodes
+ */
+
+class Sync
+{
+private:
+  RF24Network& network;
+  uint8_t* app_data; /**< Application's copy of the data */
+  uint8_t* internal_data; /**< Our copy of the data */
+  size_t len; /**< Length of the data in bytes */
+  uint16_t to_node; /**< The other node we're syncing with */
+
+protected:
+public:
+  /**
+   * Constructor
+   *
+   * @param _network Which network to syncrhonize over
+   */
+  Sync(RF24Network& _network): network(_network), app_data(NULL),
+    internal_data(NULL), len(0), to_node(0)
+  {
+  }
+  /**
+   * Begin the object
+   *
+   * @param _to_node Which node we are syncing with
+   */
+  void begin(uint16_t _to_node)
+  {
+    to_node = _to_node;
+  }
+  /**
+   * Declare the shared data set
+   *
+   * @param _data Location of shared data to be syncrhonized
+   */
+  template <class T>
+  void register_me(T& _data)
+  {
+    app_data = reinterpret_cast<uint8_t*>(&_data);
+    len = sizeof(_data);
+    internal_data = reinterpret_cast<uint8_t*>(malloc(len));
+    reset();
+  }
+
+  /**
+   * Reset the internal copy of the shared data set 
+   */
+  void reset(void)
+  {
+    memcpy(internal_data,app_data,len);
+  }
+  
+  /**
+   * Update the network and the shared data set
+   */
+  void update(void);
+};
+
+#endif // __SYNC_H__
+// vim:cin:ai:sts=2 sw=2 ft=cpp