Ported Arduino RF24Network library.
Revision 0:df0a8886a0e9, committed 2015-03-09
- Comitter:
- zcw607
- Date:
- Mon Mar 09 20:49:13 2015 +0000
- Child:
- 1:b5836d9f8de5
- Commit message:
- ported Arduino RF24Network
Changed in this revision
--- /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