Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: xtoff3 CYS_Receiver
Fork of RF24Network by
Revision 0:c3db0798d9aa, committed 2015-07-06
- Comitter:
- akashvibhute
- Date:
- Mon Jul 06 05:11:06 2015 +0000
- Child:
- 1:caf146ffe8b0
- Commit message:
- working RF24Network maniacbug library; tested on nucleo f411
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RF24Network.cpp Mon Jul 06 05:11:06 2015 +0000
@@ -0,0 +1,439 @@
+/*
+ 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);
+
+ radio.setAutoAck(1); /*****/
+
+ // 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
+ bool 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),("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 ) | 7;
+ 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 & 7;
+}
+
+/******************************************************************/
+
+bool is_valid_address( uint16_t node )
+{
+ bool result = true;
+
+ while(node)
+ {
+ uint8_t digit = node & 7;
+ if (digit < 1 || digit > 5)
+ {
+ result = false;
+ //printf_P(("*** 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
+
+uint8_t RF24Network::min(uint8_t a, uint8_t b)
+{
+ if(a < b)
+ return a;
+ else
+ return b;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RF24Network.h Mon Jul 06 05:11:06 2015 +0000
@@ -0,0 +1,344 @@
+/*
+ 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 */
+ uint8_t min(uint8_t, uint8_t);
+};
+
+/**
+ * @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 Jul 06 05:11:06 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 Jul 06 05:11:06 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 Jul 06 05:11:06 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
