Initial import of support files for all Digistump boards - Digispark, Pro, DigiX - including libraries, examples, tools, and other support files for the Arduino IDE

This commit is contained in:
Erik Tylek Kettenburg
2014-12-19 08:45:50 -08:00
parent 97abdbf157
commit 7e7473a2d6
3567 changed files with 722870 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
version.h
output/
ojam/
.*.swp

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
# (1) Project Information
PROJECT_LIBS = SPI RF24 RF24Network ;
# (2) Board Information
UPLOAD_PROTOCOL ?= stk500v1 ;
UPLOAD_SPEED ?= 57600 ;
MCU ?= atmega328p ;
F_CPU ?= 16000000 ;
CORE ?= arduino ;
VARIANT ?= standard ;
ARDUINO_VERSION ?= 100 ;
# (3) USB Ports
PORTS = p4 p6 p9 u0 u1 u2 ;
PORT_p6 = /dev/tty.usbserial-A600eHIs ;
PORT_p4 = /dev/tty.usbserial-A40081RP ;
PORT_p9 = /dev/tty.usbserial-A9007LmI ;
PORT_u0 = /dev/ttyUSB0 ;
PORT_u1 = /dev/ttyUSB1 ;
PORT_u2 = /dev/ttyUSB2 ;
# (4) Location of AVR tools
#
# This configuration assumes using avr-tools that were obtained separate from the Arduino
# distribution.
if $(OS) = MACOSX
{
AVR_BIN = /usr/local/avrtools/bin ;
AVR_ETC = /usr/local/avrtools/etc ;
AVR_INCLUDE = /usr/local/avrtools/include ;
}
else
{
AVR_BIN = /usr/bin ;
AVR_INCLUDE = /usr/lib/avr/include ;
AVR_ETC = /etc ;
}
# (5) Directories where Arduino core and libraries are located
ARDUINO_DIR ?= /opt/Arduino ;
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ;
ARDUINO_LIB = $(ARDUINO_DIR)/libraries ;
SKETCH_LIB = $(HOME)/Source/Arduino/libraries ;
#
# --------------------------------------------------
# Below this line usually never needs to be modified
#
# Tool locations
CC = $(AVR_BIN)/avr-gcc ;
C++ = $(AVR_BIN)/avr-g++ ;
LINK = $(AVR_BIN)/avr-gcc ;
OBJCOPY = $(AVR_BIN)/avr-objcopy ;
AVRDUDE = $(AVR_BIN)/avrdude ;
# Flags
DEFINES += NODE=$(NODE) F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ;
OPTIM = -Os ;
CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ;
C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ;
LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ;
AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ;
# Search everywhere for headers
HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ;
# Output locations
LOCATE_TARGET = $(F_CPU) ;
LOCATE_SOURCE = $(F_CPU) ;
#
# Custom rules
#
rule GitVersion
{
Always $(<) ;
Depends all : $(<) ;
}
actions GitVersion
{
echo "const char program_version[] = \"\\" > $(<)
git log -1 --pretty=format:%h >> $(<)
echo "\";" >> $(<)
}
GitVersion version.h ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule UserObject
{
switch $(>:S)
{
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
}
}
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
rule Main
{
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex -R .eeprom $(>) $(<)
}
rule Upload
{
Depends $(1) : $(2) ;
Depends $(2) : $(3) ;
NotFile $(1) ;
Always $(1) ;
Always $(2) ;
UploadAction $(2) : $(3) ;
}
actions UploadAction
{
$(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i
}
#
# Targets
#
# Grab everything from the core directory
CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ;
# Grab everything from libraries. To avoid this "grab everything" behaviour, you
# can specify specific modules to pick up in PROJECT_MODULES
LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ;
# Grab everything from the current dir
PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ;
# Main output executable
MAIN = $(PWD:B).elf ;
Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ;
Hex $(MAIN:B).hex : $(MAIN) ;
# Upload targets
for _p in $(PORTS)
{
Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ;
}

View File

@@ -0,0 +1,3 @@
# Network Layer for nRF24L01(+) radios
Please see the full documentation at http://maniacbug.github.com/RF24Network/index.html

View File

@@ -0,0 +1,440 @@
/*
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);
}
/******************************************************************/
uint16_t RF24Network::parent() const
{
if ( node_address == 0 )
return -1;
else
return parent_node;
}
/******************************************************************/
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(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 ) | 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

View File

@@ -0,0 +1,350 @@
/*
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);
/**
* This node's parent address
*
* @return This node's parent address, or -1 if this is the base
*/
uint16_t parent() const;
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

View File

@@ -0,0 +1,72 @@
/*
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__
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#include <stddef.h>
// Stuff that is normally provided by Arduino
#ifndef ARDUINO
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern HardwareSPI SPI;
#define _BV(x) (1<<(x))
#endif
// Define _BV for non-Arduino platforms and for Arduino DUE
#if ! defined(ARDUINO) || (defined(ARDUINO) && defined(__arm__))
#define _BV(x) (1<<(x))
#endif
#undef SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define IF_SERIAL_DEBUG(x) ({x;})
#else
#define IF_SERIAL_DEBUG(x)
#endif
// Avoid spurious warnings
// Arduino DUE is arm and uses traditional PROGMEM constructs
#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__)
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
// Progmem is Arduino-specific
// Arduino DUE is arm and does not include avr/pgmspace
#if defined(ARDUINO) && ! defined(__arm__)
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
#if ! defined(ARDUINO) // This doesn't work on Arduino DUE
typedef char const char;
#else // Fill in pgm_read_byte that is used, but missing from DUE
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
#define strlen_P strlen
#define PROGMEM
#define pgm_read_word(p) (*(p))
#define PRIPSTR "%s"
#endif
#endif // __RF24_CONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
SubDir TOP libraries RF24Network examples sensornet ;
LOCATE_SOURCE = $(SUBDIR)/$(PINS) ;
LOCATE_TARGET = $(SUBDIR)/$(PINS) ;
ArduinoWithLibs Main : RF24Network RF24 Tictocs SPI ;

View File

@@ -0,0 +1,4 @@
version.h
output/
ojam/
.*.swp

View File

@@ -0,0 +1,6 @@
SubDir TOP libraries RF24Network examples helloworld_rx ;
LOCATE_SOURCE = $(SUBDIR)/$(PINS) ;
LOCATE_TARGET = $(SUBDIR)/$(PINS) ;
ArduinoWithLibs Main : RF24Network RF24 Tictocs SPI ;

View File

@@ -0,0 +1,67 @@
/*
Copyright (C) 2012 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.
*/
/**
* Simplest possible example of using RF24Network,
*
* RECEIVER NODE
* Listens for messages from the transmitter and prints them out.
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9,10);
// Network uses that radio
RF24Network network(radio);
// Address of our node
const uint16_t this_node = 0;
// Address of the other node
const uint16_t other_node = 1;
// Structure of our payload
struct payload_t
{
unsigned long ms;
unsigned long counter;
};
void setup(void)
{
Serial.begin(57600);
Serial.println("RF24Network/examples/helloworld_rx/");
SPI.begin();
radio.begin();
network.begin(/*channel*/ 90, /*node address*/ this_node);
}
void loop(void)
{
// Pump the network regularly
network.update();
// Is there anything ready for us?
while ( network.available() )
{
// If so, grab it and print it out
RF24NetworkHeader header;
payload_t payload;
network.read(header,&payload,sizeof(payload));
Serial.print("Received packet #");
Serial.print(payload.counter);
Serial.print(" at ");
Serial.println(payload.ms);
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,4 @@
version.h
output/
ojam/
.*.swp

View File

@@ -0,0 +1,6 @@
SubDir TOP libraries RF24Network examples helloworld_tx ;
LOCATE_SOURCE = $(SUBDIR)/$(PINS) ;
LOCATE_TARGET = $(SUBDIR)/$(PINS) ;
ArduinoWithLibs Main : RF24Network RF24 Tictocs SPI ;

View File

@@ -0,0 +1,79 @@
/*
Copyright (C) 2012 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.
*/
/**
* Simplest possible example of using RF24Network
*
* TRANSMITTER NODE
* Every 2 seconds, send a payload to the receiver node.
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9,10);
// Network uses that radio
RF24Network network(radio);
// Address of our node
const uint16_t this_node = 1;
// Address of the other node
const uint16_t other_node = 0;
// How often to send 'hello world to the other unit
const unsigned long interval = 2000; //ms
// When did we last send?
unsigned long last_sent;
// How many have we sent already
unsigned long packets_sent;
// Structure of our payload
struct payload_t
{
unsigned long ms;
unsigned long counter;
};
void setup(void)
{
Serial.begin(57600);
Serial.println("RF24Network/examples/helloworld_tx/");
SPI.begin();
radio.begin();
network.begin(/*channel*/ 90, /*node address*/ this_node);
}
void loop(void)
{
// Pump the network regularly
network.update();
// If it's time to send a message, send it!
unsigned long now = millis();
if ( now - last_sent >= interval )
{
last_sent = now;
Serial.print("Sending...");
payload_t payload = { millis(), packets_sent++ };
RF24NetworkHeader header(/*to node*/ other_node);
bool ok = network.write(header,&payload,sizeof(payload));
if (ok)
Serial.println("ok.");
else
Serial.println("failed.");
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,12 @@
SubDir TOP ;
# Set up output directories
LOCATE_TARGET = $(SEARCH_SOURCE)/out/$(TOOLSET) ;
LOCATE_SOURCE = $(LOCATE_TARGET) ;
# Pull in local libraries
SKETCH_LIBS += RF24Network RF24 ;
HDRS += $(HOME)/Source/Arduino/libraries/$(SKETCH_LIBS) ;
# Main output executable
Maple $(SEARCH_SOURCE:B).elf : [ GLOB $(SEARCH_SOURCE) $(HOME)/Source/Arduino/libraries/$(SKETCH_LIBS) : $(MODULE_EXT) ] ;

View File

@@ -0,0 +1,237 @@
MCU = cortex-m3 ;
CHIP = STM32F103ZE ;
BOARD = maple_native ;
#CHIP = at91sam3u4 ;
#BOARD = sam3u-ek ;
if ! $(TOOLSET)
{
TOOLSET = mix ;
Echo "Assuming TOOLSET=mix" ;
}
else if $(TOOLSET) = cs
{
TOOLS_PATH = /opt/cs/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
else if $(TOOLSET) = yagarto
{
TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
if $(TOOLSET) = yagarto-install
{
TOOLS_PATH = ~/Source/yagarto/install/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
else if $(TOOLSET) = devkit
{
TOOLS_PATH = /opt/devkitARM/bin ;
TOOLS_ARCH = arm-eabi- ;
}
else if $(TOOLSET) = maple
{
TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
LINK_DIR = /opt/Maple/Resources/Java/hardware/leaflabs/cores/maple ;
MAPLE_DIR = /opt/Maple/Resources/Java/hardware/leaflabs/cores/maple ;
MAPLE_SUBDIRS = . ;
}
else if $(TOOLSET) = mix
{
TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
LINKFLAGS += --print-gc-sections ;
}
else if $(TOOLSET) = ports
{
TOOLS_PATH = /opt/local/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
# Tool locations
CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ;
C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ;
AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ;
LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ;
OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ;
DFU = dfu-util ;
# Flags
DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ;
OPTIM = -Os ;
MFLAGS = cpu=$(MCU) thumb arch=armv7-m ;
CCFLAGS = -Wall -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ;
C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ;
LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ;
DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ;
# Directories
MAPLE_DIR ?= /opt/libmaple ;
MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ;
MAPLE_SUBDIRS ?= libmaple libmaple/usb libmaple/usb/usb_lib wirish wirish/comm wirish/boards ;
SKETCH_DIR = $(HOME)/Source/Maple ;
CORE_DIRS = $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) ;
HDRS = $(CORE_DIRS) ;
# Modules
MODULE_EXT = *.c *.cpp *.S *.pde *.ino *.test ;
CORE_MODULES = [ GLOB $(CORE_DIRS) : $(MODULE_EXT) ] ;
# Unit test framework
CXXTEST_DIR = $(HOME)/Source/cxxtest ;
#CXXTEST_GEN = $(CXXTEST_DIR)/cxxtestgen.py ;
CXXTEST_GEN = $(CXXTEST_DIR)/python/scripts/cxxtestgen ;
HDRS += $(CXXTEST_DIR) ;
# Linker script
LINK_DIR ?= $(MAPLE_DIR)/support/ld ;
LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ;
LINKFLAGS += -T$(LINKSCRIPT) -L$(LINK_DIR) ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule TestSuite
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
actions TestSuite
{
$(CXXTEST_GEN) --part $(>) > $(<)
}
rule TestRoot
{
MakeLocate $(<) : $(LOCATE_SOURCE) ;
}
actions TestRoot
{
$(CXXTEST_GEN) --root --error-printer > $(<)
}
rule C++TestSuite
{
local _CPP = $(>:B).cpp ;
TestSuite $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex $(>) $(<)
}
rule Binary
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends binary : $(<) ;
Clean clean : $(<) ;
}
actions Binary
{
$(OBJCOPY) -O binary $(>) $(<)
}
rule UserObject
{
switch $(>:S)
{
case .S : As $(<) : $(>) ;
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
case .test : C++TestSuite $(<) : $(>) ;
}
}
rule Upload
{
Depends up : $(<) ;
NotFile up ;
Always $(<) ;
Always up ;
}
actions Upload
{
$(DFU) $(DFUFLAGS) -D $(<)
}
# Override base objects rule, so all output can go in the output dir
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
# Override base main rule, so all output can go in the output dir
rule Main
{
# Bring in the map
LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ;
MakeLocate $(<) ;
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Maple
{
Main $(<) : $(>) $(CORE_MODULES) ;
Binary $(<:B).bin : $(<) ;
Upload $(<:B).bin ;
}

View File

@@ -0,0 +1,106 @@
/*
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.
*/
/**
* Simplest possible example of using RF24Network
*
* TRANSMITTER NODE
* Every 2 seconds, says hello to the receiver node.
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
//
// Maple specific setup. Other than this section, the sketch is the same on Maple as on
// Arduino
//
#ifdef MAPLE_IDE
// External startup function
extern void board_start(const char* program_name);
// Use SPI #2.
HardwareSPI SPI(2);
inline void serial_begin(int _baud)
{
}
#else
inline void serial_begin(int _baud)
{
Serial.begin(_baud);
}
#define board_startup printf
#define toggleLED(x) (x)
#endif
//
// Hardware configuration
//
// Set up nRF24L01 radio on SPI bus plus pins 7 & 6
// (This works for the Getting Started board plugged into the
// Maple Native backwards.)
RF24 radio(7,6);
// Network uses that radio
RF24Network network(radio);
// Address of our node
const uint16_t this_node = 1;
// Address of the other node
const uint16_t other_node = 0;
// How often to send 'hello world to the other unit
const unsigned long interval = 2000; //ms
// When did we last send?
unsigned long last_sent;
void setup(void)
{
serial_begin(57600);
board_start("RF24Network/examples/helloworld_tx/");
SPI.begin();
radio.begin();
network.begin(/*channel*/ 90, /*node address*/ this_node);
}
void loop(void)
{
// Pump the network regularly
network.update();
// If it's time to send a message, send it!
unsigned long now = millis();
if ( now - last_sent > interval )
{
last_sent = now;
toggleLED();
printf("Sending...\r\n");
const char* hello = "Hello, world!";
RF24NetworkHeader header(/*to node*/ other_node);
bool ok = network.write(header,hello,strlen(hello));
if (ok)
printf("\tok.\r\n");
else
{
printf("\tfailed.\r\n");
delay(250); // extra delay on fail to keep light on longer
}
toggleLED();
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,77 @@
#ifdef MAPLE_IDE
#include <stdio.h>
#include "wirish.h"
extern void setup(void);
extern void loop(void);
void board_start(const char* program_name)
{
// Set up the LED to steady on
pinMode(BOARD_LED_PIN, OUTPUT);
digitalWrite(BOARD_LED_PIN, HIGH);
// Setup the button as input
pinMode(BOARD_BUTTON_PIN, INPUT);
digitalWrite(BOARD_BUTTON_PIN, HIGH);
SerialUSB.begin();
SerialUSB.println("Press BUT");
// Wait for button press
while ( !isButtonPressed() )
{
}
SerialUSB.println("Welcome!");
SerialUSB.println(program_name);
int i = 11;
while (i--)
{
toggleLED();
delay(50);
}
}
/**
* Custom version of _write, which will print to the USB.
* In order to use it you MUST ADD __attribute__((weak))
* to _write in libmaple/syscalls.c
*/
extern "C" int _write(int file, char * ptr, int len)
{
if ( (file != 1) && (file != 2) )
return 0;
else
SerialUSB.write(ptr,len);
return len;
}
__attribute__((constructor)) __attribute__ ((weak)) void premain()
{
init();
}
__attribute__((weak)) void setup(void)
{
board_start("No program defined");
}
__attribute__((weak)) void loop(void)
{
}
__attribute__((weak)) int main(void)
{
setup();
while (true)
{
loop();
}
return 0;
}
#endif // ifdef MAPLE_IDE
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,4 @@
version.h
output/
ojam/
.*.swp

View File

@@ -0,0 +1,6 @@
SubDir TOP libraries RF24Network examples meshping ;
LOCATE_SOURCE = $(SUBDIR)/$(PINS) ;
LOCATE_TARGET = $(SUBDIR)/$(PINS) ;
ArduinoWithLibs Main : RF24Network RF24 Tictocs SPI ;

View File

@@ -0,0 +1,260 @@
/*
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.
*/
/**
* Example of pinging across a mesh network
*
* Using this sketch, each node will send a ping to every other node
* in the network every few seconds.
* The RF24Network library will route the message across
* the mesh to the correct node.
*
* This sketch is greatly complicated by the fact that at startup time, each
* node (including the base) has no clue what nodes are alive. So,
* each node builds an array of nodes it has heard about. The base
* periodically sends out its whole known list of nodes to everyone.
*
* To see the underlying frames being relayed, compile RF24Network with
* #define SERIAL_DEBUG.
*
* The logical node address of each node is set in EEPROM. The nodeconfig
* module handles this by listening for a digit (0-9) on the serial port,
* and writing that number to EEPROM.
*
* To use the sketch, upload it to two or more units. Run each one in
* turn. Attach a serial monitor, and send a single-digit address to
* each. Make the first one '0', and the following units '1', '2', etc.
*/
#include <avr/pgmspace.h>
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include "nodeconfig.h"
#include "printf.h"
// This is for git version tracking. Safe to ignore
#ifdef VERSION_H
#include "version.h"
#else
#define __TAG__ "Unknown"
#endif
// nRF24L01(+) radio using the Getting Started board
RF24 radio(9,10);
RF24Network network(radio);
// Our node address
uint16_t this_node;
// Delay manager to send pings regularly
const unsigned long interval = 2000; // ms
unsigned long last_time_sent;
// Array of nodes we are aware of
const short max_active_nodes = 10;
uint16_t active_nodes[max_active_nodes];
short num_active_nodes = 0;
short next_ping_node_index = 0;
// Prototypes for functions to send & handle messages
bool send_T(uint16_t to);
bool send_N(uint16_t to);
void handle_T(RF24NetworkHeader& header);
void handle_N(RF24NetworkHeader& header);
void add_node(uint16_t node);
void setup(void)
{
//
// Print preamble
//
Serial.begin(57600);
printf_begin();
printf_P(PSTR("\n\rRF24Network/examples/meshping/\n\r"));
printf_P(PSTR("VERSION: " __TAG__ "\n\r"));
//
// Pull node address out of eeprom
//
// Which node are we?
this_node = nodeconfig_read();
//
// Bring up the RF network
//
SPI.begin();
radio.begin();
network.begin(/*channel*/ 100, /*node address*/ this_node );
}
void loop(void)
{
// Pump the network regularly
network.update();
// Is there anything ready for us?
while ( network.available() )
{
// If so, take a look at it
RF24NetworkHeader header;
network.peek(header);
// Dispatch the message to the correct handler.
switch (header.type)
{
case 'T':
handle_T(header);
break;
case 'N':
handle_N(header);
break;
default:
printf_P(PSTR("*** WARNING *** Unknown message type %c\n\r"),header.type);
network.read(header,0,0);
break;
};
}
// Send a ping to the next node every 'interval' ms
unsigned long now = millis();
if ( now - last_time_sent >= interval )
{
last_time_sent = now;
// Who should we send to?
// By default, send to base
uint16_t to = 00;
// Or if we have active nodes,
if ( num_active_nodes )
{
// Send to the next active node
to = active_nodes[next_ping_node_index++];
// Have we rolled over?
if ( next_ping_node_index > num_active_nodes )
{
// Next time start at the beginning
next_ping_node_index = 0;
// This time, send to node 00.
to = 00;
}
}
bool ok;
// Normal nodes send a 'T' ping
if ( this_node > 00 || to == 00 )
ok = send_T(to);
// Base node sends the current active nodes out
else
ok = send_N(to);
// Notify us of the result
if (ok)
{
printf_P(PSTR("%lu: APP Send ok\n\r"),millis());
}
else
{
printf_P(PSTR("%lu: APP Send failed\n\r"),millis());
// Try sending at a different time next time
last_time_sent -= 100;
}
}
// Listen for a new node address
nodeconfig_listen();
}
/**
* Send a 'T' message, the current time
*/
bool send_T(uint16_t to)
{
RF24NetworkHeader header(/*to node*/ to, /*type*/ 'T' /*Time*/);
// The 'T' message that we send is just a ulong, containing the time
unsigned long message = millis();
printf_P(PSTR("---------------------------------\n\r"));
printf_P(PSTR("%lu: APP Sending %lu to 0%o...\n\r"),millis(),message,to);
return network.write(header,&message,sizeof(unsigned long));
}
/**
* Send an 'N' message, the active node list
*/
bool send_N(uint16_t to)
{
RF24NetworkHeader header(/*to node*/ to, /*type*/ 'N' /*Time*/);
printf_P(PSTR("---------------------------------\n\r"));
printf_P(PSTR("%lu: APP Sending active nodes to 0%o...\n\r"),millis(),to);
return network.write(header,active_nodes,sizeof(active_nodes));
}
/**
* Handle a 'T' message
*
* Add the node to the list of active nodes
*/
void handle_T(RF24NetworkHeader& header)
{
// The 'T' message is just a ulong, containing the time
unsigned long message;
network.read(header,&message,sizeof(unsigned long));
printf_P(PSTR("%lu: APP Received %lu from 0%o\n\r"),millis(),message,header.from_node);
// If this message is from ourselves or the base, don't bother adding it to the active nodes.
if ( header.from_node != this_node || header.from_node > 00 )
add_node(header.from_node);
}
/**
* Handle an 'N' message, the active node list
*/
void handle_N(RF24NetworkHeader& header)
{
static uint16_t incoming_nodes[max_active_nodes];
network.read(header,&incoming_nodes,sizeof(incoming_nodes));
printf_P(PSTR("%lu: APP Received nodes from 0%o\n\r"),millis(),header.from_node);
int i = 0;
while ( i < max_active_nodes && incoming_nodes[i] > 00 )
add_node(incoming_nodes[i++]);
}
/**
* Add a particular node to the current list of active nodes
*/
void add_node(uint16_t node)
{
// Do we already know about this node?
short i = num_active_nodes;
while (i--)
{
if ( active_nodes[i] == node )
break;
}
// If not, add it to the table
if ( i == -1 && num_active_nodes < max_active_nodes )
{
active_nodes[num_active_nodes++] = node;
printf_P(PSTR("%lu: APP Added 0%o to list of active nodes.\n\r"),millis(),node);
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,69 @@
/*
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 <avr/eeprom.h>
#include <avr/pgmspace.h>
#include "nodeconfig.h"
// Where in EEPROM is the address stored?
uint8_t* address_at_eeprom_location = (uint8_t*)10;
// What flag value is stored there so we know the value is valid?
const uint8_t valid_eeprom_flag = 0xdf;
// What are the actual node values that we want to use?
// EEPROM locations are actually just indices into this array
const uint16_t node_address_set[10] = { 00, 02, 05, 012, 015, 022, 025, 032, 035, 045 };
uint8_t nodeconfig_read(void)
{
uint8_t result = 0;
// Look for the token in EEPROM to indicate the following value is
// a validly set node address
if ( eeprom_read_byte(address_at_eeprom_location) == valid_eeprom_flag )
{
// Read the address from EEPROM
result = node_address_set[ eeprom_read_byte(address_at_eeprom_location+1) ];
printf_P(PSTR("ADDRESS: %u\n\r"),result);
}
else
{
printf_P(PSTR("*** No valid address found. Send 0-9 via serial to set node address\n\r"));
while(1)
{
nodeconfig_listen();
}
}
return result;
}
void nodeconfig_listen(void)
{
//
// Listen for serial input, which is how we set the address
//
if (Serial.available())
{
// If the character on serial input is in a valid range...
char c = Serial.read();
if ( c >= '0' && c <= '9' )
{
// It is our address
eeprom_write_byte(address_at_eeprom_location,valid_eeprom_flag);
eeprom_write_byte(address_at_eeprom_location+1,c-'0');
// And we are done right now (no easy way to soft reset)
printf_P(PSTR("\n\rManually reset index to: %c, address 0%o\n\rPress RESET to continue!"),c,node_address_set[c-'0']);
while(1);
}
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,15 @@
/*
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 __NODECONFIG_H__
#define __NODECONFIG_H__
uint8_t nodeconfig_read(void);
void nodeconfig_listen(void);
#endif // __NODECONFIG_H__

View File

@@ -0,0 +1,37 @@
/*
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.
*/
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#ifdef ARDUINO
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif // __PRINTF_H__

View File

@@ -0,0 +1,4 @@
version.h
output/
ojam/
.*.swp

View File

@@ -0,0 +1,30 @@
#ifndef __DUINODE_V3_H__
#define __DUINODE_V3_H__
/**
* @file DuinodeV1.h
*
* Contains hardware definitions for RF Duinode V1 (3V3)
*/
#define PINS_DEFINED 1
#define __PLATFORM__ "RF Duinode V1 (3V3)"
const int rf_irq = 0;
const int led_red = 0;
const int led_yellow = 0;
const int led_green = 0;
const int button_a = 0;
const int rf_ce = 8;
const int rf_csn = 9;
const int temp_pin = 2; // analog
const int voltage_pin = 3; // analog
// 1.1V internal reference after 1M/470k divider, in 8-bit fixed point
const unsigned voltage_reference = 0x371;
#endif // __DUINODE_V3_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,30 @@
#ifndef __DUINODE_V3_H__
#define __DUINODE_V3_H__
/**
* @file DuinodeV3.h
*
* Contains hardware definitions for RF Duinode V3 (2V4)
*/
#define PINS_DEFINED 1
#define __PLATFORM__ "RF Duinode V3/V4 (2V4)"
const int rf_irq = 0;
const int led_red = 3;
const int led_yellow = 4;
const int led_green = 5;
const int button_a = 6;
const int rf_ce = 8;
const int rf_csn = 7;
const int temp_pin = 2; // analog
const int voltage_pin = 3; // analog
// 1.1V internal reference after 1M/470k divider, in 8-bit fixed point
const unsigned voltage_reference = 0x371;
#endif // __DUINODE_V3_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,30 @@
#ifndef __DUINODE_V5_H__
#define __DUINODE_V5_H__
/**
* @file DuinodeV3.h
*
* Contains hardware definitions for RF Duinode V5 (2V4)
*/
#define PINS_DEFINED 1
#define __PLATFORM__ "RF Duinode V5 (2V4)"
const int rf_irq = 0;
const int led_red = 3;
const int led_yellow = 4;
const int led_green = 5;
const int button_a = 6;
const int rf_ce = 14;
const int rf_csn = 15;
const int temp_pin = 2; // analog
const int voltage_pin = 3; // analog
// 1.1V internal reference after 1M/470k divider, in 8-bit fixed point
const unsigned voltage_reference = 0x371;
#endif // __DUINODE_V5_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,6 @@
SubDir TOP libraries RF24Network examples sensornet ;
LOCATE_SOURCE = $(SUBDIR)/$(PINS) ;
LOCATE_TARGET = $(SUBDIR)/$(PINS) ;
ArduinoWithLibs Main : RF24Network RF24 Tictocs SPI ;

View File

@@ -0,0 +1,34 @@
/*
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
// Framework headers
// Library headers
#include "RF24Network_config.h"
// Project headers
// This component's header
#include "S_message.h"
char S_message::buffer[32];
/****************************************************************************/
char* S_message::toString(void)
{
snprintf(buffer,sizeof(buffer),"%2u.%02uC /%2u.%02uV",
temp_reading >> 8,
( temp_reading & 0xFF ) * 100 / 256,
voltage_reading >> 8,
( voltage_reading & 0xFF ) * 100 / 256
);
return buffer;
}
/****************************************************************************/
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,32 @@
/*
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 __S_MESSAGE_H__
#define __S_MESSAGE_H__
// STL headers
// C headers
// Framework headers
// Library headers
// Project headers
/**
* Sensor message (type 'S')
*/
struct S_message
{
uint16_t temp_reading;
uint16_t voltage_reading;
static char buffer[];
S_message(void): temp_reading(0), voltage_reading(0) {}
char* toString(void);
};
#endif // __S_MESSAGE_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1 @@
jam F_CPU=16000000 "CCFLAGS=-include DuinodeV1.h" -dx UPLOAD_SPEED=115200 u1 && screen /dev/ttyUSB1 57600

View File

@@ -0,0 +1,3 @@
AVR_BIN=/usr/local/avr/bin AVR_ETC=/usr/local/avr/etc jam F_CPU=8000000 sensornet.elf
touch sensornet.pde
AVR_BIN=/usr/local/avr/bin AVR_ETC=/usr/local/avr/etc jam F_CPU=8000000 "CCFLAGS=-include DuinodeV3.h" -dx UPLOAD_SPEED=57600 u1 && screen /dev/ttyUSB1 57600

View File

@@ -0,0 +1 @@
jam F_CPU=8000000 "CCFLAGS=-include DuinodeV5.h" -dx UPLOAD_SPEED=57600 u0 && picocom -b 57600 /dev/ttyUSB0

View File

@@ -0,0 +1,131 @@
/*
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 <avr/eeprom.h>
#include <avr/pgmspace.h>
#include "nodeconfig.h"
// Where in EEPROM is the address stored?
uint8_t* address_at_eeprom_location = (uint8_t*)10;
eeprom_info_t eeprom_info;
const eeprom_info_t& nodeconfig_read(void)
{
eeprom_read_block(&eeprom_info,address_at_eeprom_location,sizeof(eeprom_info));
// Look for the token in EEPROM to indicate the following value is
// a validly set node address
if ( eeprom_info.isValid() )
{
printf_P(PSTR("ADDRESS: %o\n\r"),eeprom_info.address);
printf_P(PSTR("ROLE: %S\n\r"),eeprom_info.relay ? PSTR("Relay") : PSTR("Leaf") );
printf_P(PSTR("TEMP: %04x\n\r"),eeprom_info.temp_calibration);
}
else
{
eeprom_info.clear();
printf_P(PSTR("*** No valid address found. Send node address via serial of the form 011<cr>\n\r"));
while(1)
{
nodeconfig_listen();
}
}
return eeprom_info;
}
char serialdata[10];
char* nextserialat = serialdata;
const char* maxserial = serialdata + sizeof(serialdata) - 1;
void nodeconfig_listen(void)
{
//
// Listen for serial input, which is how we set the address
//
if (Serial.available())
{
// If the character on serial input is in a valid range...
char c = tolower(Serial.read());
if ( (c >= '0' && c <= '9' ) || (c >= 'a' && c <= 'f' ) )
{
*nextserialat++ = c;
if ( nextserialat == maxserial )
{
*nextserialat = 0;
printf_P(PSTR("\r\n*** Unknown serial command: %s\r\n"),serialdata);
nextserialat = serialdata;
}
}
else if ( tolower(c) == 'r' )
{
eeprom_info.relay = true;
printf_P(PSTR("ROLE: %S\n\r"),eeprom_info.relay ? PSTR("Relay") : PSTR("Leaf") );
eeprom_update_block(&eeprom_info,address_at_eeprom_location,sizeof(eeprom_info));
printf_P(PSTR("RESET NODE before changes take effect\r\n"));
if ( ! eeprom_info.isValid() )
printf_P(PSTR("Please assign an address\r\n"));
}
else if ( tolower(c) == 'l' )
{
eeprom_info.relay = false;
printf_P(PSTR("ROLE: %S\n\r"),eeprom_info.relay ? PSTR("Relay") : PSTR("Leaf") );
eeprom_update_block(&eeprom_info,address_at_eeprom_location,sizeof(eeprom_info));
printf_P(PSTR("RESET NODE before changes take effect\r\n"));
if ( ! eeprom_info.isValid() )
printf_P(PSTR("Please assign an address\r\n"));
}
else if ( tolower(c) == 't' )
{
// Send temperature calibration as 2-digit 4.4-fixed decimal signed degrees
// celcius--followed by a 't'. So, for -1.5 degrees, send "e8t". Or for +2 degrees
// send "20t"
*nextserialat = 0;
int8_t val = strtol(serialdata,NULL,16);
nextserialat = serialdata;
set_temp_calibration( val * 0x10 );
printf_P(PSTR("RESET NODE before changes take effect\r\n"));
if ( ! eeprom_info.isValid() )
printf_P(PSTR("Please assign an address\r\n"));
}
else if ( c == 13 )
{
// Convert to octal
char *pc = serialdata;
uint16_t address = 0;
while ( pc < nextserialat )
{
address <<= 3;
address |= (*pc++ - '0');
}
// It is our address
eeprom_info.address = address;
eeprom_update_block(&eeprom_info,address_at_eeprom_location,sizeof(eeprom_info));
// And we are done right now (no easy way to soft reset)
printf_P(PSTR("\n\rManually set to address 0%o\n\rPress RESET to continue!"),address);
while(1);
}
}
}
void set_temp_calibration(int16_t val)
{
eeprom_info.temp_calibration = val;
printf_P(PSTR("TEMP: %02x\n\r"),eeprom_info.temp_calibration);
eeprom_update_block(&eeprom_info,address_at_eeprom_location,sizeof(eeprom_info));
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,49 @@
/*
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 __NODECONFIG_H__
#define __NODECONFIG_H__
// Additional info
struct eeprom_info_t
{
uint8_t flag;
uint16_t address;
int16_t temp_calibration; // sensor adjustment in signed fixed-8.8 degrees, e.g. 0xFE80 is -1.5
bool relay:1;
static const uint8_t valid_flag = 0xdd;
eeprom_info_t()
{
clear();
}
bool isValid() const
{
return (flag == valid_flag) && (address != 0xffff);
}
void clear()
{
flag = valid_flag;
address = 0xffff;
relay = false;
temp_calibration = 0;
}
};
const eeprom_info_t& nodeconfig_read(void);
void nodeconfig_listen(void);
void set_temp_calibration(int16_t val);
#endif // __NODECONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,37 @@
/*
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.
*/
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#ifdef ARDUINO
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif // __PRINTF_H__

View File

@@ -0,0 +1,498 @@
/*
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.
*/
/**
* 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.
*
* The example uses TWO sensors, a 'temperature' sensor and a 'voltage'
* sensor.
*
* To see the underlying frames being relayed, compile RF24Network with
* #define SERIAL_DEBUG.
*
* The logical node address of each node is set in EEPROM. The nodeconfig
* module handles this by listening for a digit (0-9) on the serial port,
* and writing that number to EEPROM.
*/
#include <avr/pgmspace.h>
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Tictocs.h>
#include <Button.h>
#include <TictocTimer.h>
#include "nodeconfig.h"
#include "sleep.h"
#include "S_message.h"
#include "printf.h"
// This is for git version tracking. Safe to ignore
#ifdef VERSION_H
#include "version.h"
#else
#define __TAG__ "Unknown"
#endif
// Pin definitions
#ifndef PINS_DEFINED
#define __PLATFORM__ "Getting Started board"
// Pins for radio
const int rf_ce = 9;
const int rf_csn = 10;
// Pins for sensors
const int temp_pin = A2;
const int voltage_pin = A3;
// Pins for status LED, or '0' for no LED connected
const int led_red = 0;
const int led_yellow = 0;
const int led_green = 0;
// Button to control modes
const int button_a = 4;
// What voltage is a reading of 1023?
const unsigned voltage_reference = 5 * 256; // 5.0V
#endif
RF24 radio(rf_ce,rf_csn);
RF24Network network(radio);
// Our node configuration
eeprom_info_t this_node;
// Sleep constants. In this example, the watchdog timer wakes up
// every 4s, and every single wakeup we power up the radio and send
// a reading. In real use, these numbers which be much higher.
// Try wdt_8s and 7 cycles for one reading per minute.> 1
const wdt_prescalar_e wdt_prescalar = wdt_8s;
const int sleep_cycles_per_transmission = 1;
// Non-sleeping nodes need a timer to regulate their sending interval
Timer send_timer(8000);
// Button controls functionality of the unit
Button ButtonA(button_a);
// Long-press button
Button ButtonLong(button_a,1000);
/**
* Convenience class for handling LEDs. Handles the case where the
* LED may not be populated on the board, so always checks whether
* the pin is valid before setting a value.
*/
class LED
{
private:
int pin;
public:
LED(int _pin): pin(_pin)
{
if (pin > 0)
{
pinMode(pin,OUTPUT);
digitalWrite(pin,LOW);
}
}
void write(bool state) const
{
if (pin > 0)
digitalWrite(pin,state?HIGH:LOW);
}
void operator=(bool state)
{
write(state);
}
};
/**
* Startup LED sequence. Lights up the LEDs in sequence first, then dims
* them in the same sequence.
*/
class StartupLEDs: public Timer
{
private:
const LED** leds;
const LED** current;
const LED** end;
bool state;
protected:
virtual void onFired(void)
{
(*current)->write(state);
++current;
if ( current >= end )
{
if ( state )
{
state = false;
current = leds;
}
else
disable();
}
}
public:
StartupLEDs(const LED** _leds, int _num): Timer(250), leds(_leds), current(_leds), end(_leds+_num), state(true)
{
}
};
/**
* Calibration LED sequence. Flashes all 3 in unison
*/
class CalibrationLEDs: public Timer
{
const LED** leds;
const LED** end;
bool state;
protected:
void write()
{
const LED** current = end;
while (current-- > leds)
(*current)->write(state);
}
virtual void onFired()
{
state = ! state;
write();
}
public:
CalibrationLEDs(const LED** _leds, int _num, unsigned long duration = 500): Timer(duration), leds(_leds), end(_leds+_num), state(false)
{
Timer::disable();
}
void begin()
{
Updatable::begin();
}
void reset()
{
state = true;
write();
Timer::reset();
}
void disable()
{
state = false;
write();
Timer::disable();
}
};
LED Red(led_red), Yellow(led_yellow), Green(led_green);
const LED* leds[] = { &Red, &Yellow, &Green };
const int num_leds = sizeof(leds)/sizeof(leds[0]);
StartupLEDs startup_leds(leds,num_leds);
CalibrationLEDs calibration_leds(leds,num_leds);
// Nodes in test mode do not sleep, but instead constantly try to send
bool test_mode = false;
// Nodes in calibration mode are looking for temperature calibration
bool calibration_mode = false;
// Helper functions to take readings
// How many measurements to take. 64*1024 = 65536, so 64 is the max we can fit in a uint16_t.
const int num_measurements = 64;
uint32_t measure_temp()
{
int i = num_measurements;
uint32_t reading = 0;
while(i--)
reading += analogRead(temp_pin);
// Convert the reading to celcius*256
// This is the formula for MCP9700.
// C = reading * 1.1
// C = ( V - 1/2 ) * 100
//
// Then adjust for the calibation value on this sensor
return ( ( ( ( reading * 0x120 ) - 0x800000 ) * 0x64 ) >> 16 ) + this_node.temp_calibration;
}
uint32_t measure_voltage()
{
// Take the voltage reading
int i = num_measurements;
uint32_t reading = 0;
while(i--)
reading += analogRead(voltage_pin);
// Convert the voltage reading to volts*256
return ( reading * voltage_reference ) >> 16;
}
struct CalibrationData
{
uint8_t measurements_remaining;
static const uint8_t measurements_needed = 16;
int16_t accumulator;
CalibrationData()
{
measurements_remaining = measurements_needed;
accumulator = 0;
}
void add(int16_t reading)
{
accumulator += reading / measurements_needed;
if ( measurements_remaining )
--measurements_remaining;
}
bool done() const
{
return measurements_remaining == 0;
}
int16_t result() const
{
return accumulator;
}
};
CalibrationData calibration_data;
void setup(void)
{
//
// Print preamble
//
Serial.begin(57600);
printf_begin();
printf_P(PSTR("\n\rRF24Network/examples/sensornet/\n\r"));
printf_P(PSTR("PLATFORM: " __PLATFORM__ "\n\r"));
printf_P(PSTR("VERSION: " __TAG__ "\n\r"));
//
// Pull node address out of eeprom
//
// Which node are we?
this_node = nodeconfig_read();
//
// Prepare sleep parameters
//
// Only the leaves sleep. Nodes 01-05 are presumed to be relay nodes.
if ( ! this_node.relay )
Sleep.begin(wdt_prescalar,sleep_cycles_per_transmission);
//
// Set up board hardware
//
ButtonA.begin();
ButtonLong.begin();
// Sensors use the stable internal 1.1V voltage
#ifdef INTERNAL1V1
analogReference(INTERNAL1V1);
#else
analogReference(INTERNAL);
#endif
// Prepare the startup sequence
send_timer.begin();
startup_leds.begin();
calibration_leds.begin();
//
// Bring up the RF network
//
SPI.begin();
radio.begin();
network.begin(/*channel*/ 92, /*node address*/ this_node.address);
}
void loop(void)
{
// Update objects
theUpdater.update();
// Pump the network regularly
network.update();
// Is there anything ready for us?
while ( network.available() )
{
// If so, grab it and print it out
RF24NetworkHeader header;
S_message message;
network.read(header,&message,sizeof(message));
printf_P(PSTR("%lu: APP Received #%u type %c %s from 0%o\n\r"),millis(),header.id,header.type,message.toString(),header.from_node);
// If we get a message, that's a little odd, because this sketch doesn't run on the
// base node. Possibly it's a test message from a child node. Possibly it's a sensor
// calibration message from a parent node.
//
// Either way, it only matters if we're NOT sleeping, and also only useful it we have
// a temp sensor
// If we have a temp sensor AND we are not sleeping
if ( temp_pin > -1 && ( ! Sleep || test_mode ) )
{
// if the received message is a test message, we can respond with a 'C' message in return
if ( header.type == 'c' )
{
// Take a reading
S_message response;
response.temp_reading = measure_temp();
response.voltage_reading = measure_voltage();
// Send it back as a calibration message
RF24NetworkHeader response_header(/*to node*/ header.from_node, /*type*/ 'C');
network.write(response_header,&response,sizeof(response));
}
else if ( header.type == 'C' )
{
// This is a calibration message. Calculate the diff
uint16_t diff = message.temp_reading - measure_temp();
printf_P(PSTR("%lu: APP Calibration received %04x diff %04x\n\r"),millis(),message.temp_reading,diff);
calibration_data.add(diff);
if ( calibration_data.done() )
{
calibration_mode = false;
calibration_leds.disable();
printf_P(PSTR("%lu: APP Stop calibration mode. Calibrate by %04x\n\r"),millis(),calibration_data.result());
// Now apply the calibration
this_node.temp_calibration += calibration_data.result();
// And save it to eeprom...
set_temp_calibration( this_node.temp_calibration );
}
}
}
}
// If we are the kind of node that sends readings,
// AND we have a temp sensor
// AND it's time to send a reading
// AND we're in the mode where we send readings...
if ( this_node.address > 0 && temp_pin > -1 && ( ( Sleep && ! test_mode ) || send_timer.wasFired() ) && ! startup_leds )
{
// Transmission beginning, TX LED ON
Yellow = true;
if ( test_mode && ! calibration_mode )
{
Green = false;
Red = false;
}
S_message message;
message.temp_reading = measure_temp();
message.voltage_reading = measure_voltage();
char message_type = 'S';
if ( calibration_mode )
message_type = 'c';
else if (test_mode )
message_type = 't';
// By default send to the base
uint16_t to_node = 0;
if ( test_mode )
// In test mode, sent to our parent.
to_node = network.parent();
printf_P(PSTR("---------------------------------\n\r"));
printf_P(PSTR("%lu: APP Sending type-%c %s to 0%o...\n\r"),millis(),message_type,message.toString(),to_node);
RF24NetworkHeader header(to_node,message_type);
bool ok = network.write(header,&message,sizeof(message));
if (ok)
{
if ( test_mode && ! calibration_mode )
Green = true;
printf_P(PSTR("%lu: APP Send ok\n\r"),millis());
}
else
{
if ( test_mode && ! calibration_mode )
Red = true;
printf_P(PSTR("%lu: APP Send failed\n\r"),millis());
}
// Transmission complete, TX LED OFF
Yellow = false;
if ( Sleep && ! test_mode )
{
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
radio.powerDown();
// Be sure to flush the serial first before sleeping, so everything
// gets printed properly
Serial.flush();
// Sleep the MCU. The watchdog timer will awaken in a short while, and
// continue execution here.
Sleep.go();
}
}
// Button
unsigned a = ButtonA.wasReleased();
if ( a && a < 500 )
{
// Pressing the button during startup sequences engages test mode.
// Pressing it after turns off test mode.
if ( startup_leds )
{
test_mode = true;
send_timer.setInterval(1000);
printf_P(PSTR("%lu: APP Start test mode\n\r"),millis());
}
else if ( calibration_mode )
{
calibration_mode = false;
calibration_leds.disable();
printf_P(PSTR("%lu: APP Stop calibration mode\n\r"),millis());
}
else if ( test_mode )
{
test_mode = false;
Green = false;
Red = false;
send_timer.setInterval(8000);
printf_P(PSTR("%lu: APP Stop test mode\n\r"),millis());
}
}
// Long press
if ( ButtonLong.wasPressed() && test_mode )
{
calibration_mode = true;
calibration_leds.reset();
calibration_data = CalibrationData();
printf_P(PSTR("%lu: APP Start calibration mode\n\r"),millis());
}
// Listen for a new node address
nodeconfig_listen();
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,55 @@
/*
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.
*/
/**
* @file sleep.cpp
*
* Sleep helpers, definitions
*/
#include "RF24Network_config.h"
#include <avr/sleep.h>
#include "sleep.h"
sleep_c Sleep;
/******************************************************************/
void sleep_c::begin(wdt_prescalar_e prescalar_in,short cycles)
{
sleep_cycles_remaining = cycles;
sleep_cycles_per_transmission = cycles;
uint8_t prescalar = min(9,(uint8_t)prescalar_in);
uint8_t wdtcsr = prescalar & 7;
if ( prescalar & 8 )
wdtcsr |= _BV(WDP3);
MCUSR &= ~_BV(WDRF);
WDTCSR = _BV(WDCE) | _BV(WDE);
WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
}
/******************************************************************/
void sleep_c::go(void)
{
while( sleep_cycles_remaining-- )
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
}
sleep_cycles_remaining = sleep_cycles_per_transmission;
}
ISR(WDT_vect) {
}
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,84 @@
/*
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.
*/
/**
* @file sleep.h
*
* Declaration of the sleep_c class.
*/
#ifndef __SLEEP_H__
#define __SLEEP_H__
/**
* Enums for the duration of the watchdog timer
*/
typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e;
/**
* Simplified sleeping handler
*/
class sleep_c
{
public:
/**
* Enable the system to be able to sleep
*
* Does not actually do any sleeping.
*
* For example, to do something roughly evert minute, configure
* it like this:
*
* @code
* Sleep.begin(wdt_8s,7);
* @endcode
*
* @param prescalar Duration of the watchdog timer interrupt.
* The system will actually sleep for this long.
* @param cycles Number of times the system will wake up before
* returning from @p go().
*/
void begin(wdt_prescalar_e prescalar,short cycles);
/**
* Go to sleep
*
* This will return after the watchdog has awoken for the number
* of times specified in begin().
*/
void go(void);
/**
* Test whether the node sleeps
*
* @retval true if the node will sleep
*/
operator bool(void) const
{
return sleep_cycles_per_transmission;
}
private:
volatile short sleep_cycles_remaining;
short sleep_cycles_per_transmission;
};
/**
* Singleton instance for general use
*
* @warning: This class is hard-coded to ONLY work with this singleton.
* Any other instances will fail.
*/
extern sleep_c Sleep;
#endif // __SLEEP_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,38 @@
/*
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 __TIMER_H__
#define __TIMER_H__
// STL headers
// C headers
// Framework headers
// Library headers
// Project headers
/**
* Simple timer
*/
struct timer_t
{
unsigned long last;
unsigned long interval;
timer_t(unsigned long _interval): interval(_interval) {}
operator bool(void)
{
unsigned long now = millis();
bool result = now - last >= interval;
if ( result )
last = now;
return result;
}
};
#endif // __TEMPLATE_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,19 @@
This is the 'receiver' for the unit tests. Run it on a node which
is NOT under test.
TODO
Send finder request needs a re-think. It needs to be re-implemented as a Tictocs::Timer, so it
doesn't block the loop().
I could also make Tictocs objects which monitor the RF24network and pull off messages that are just
for them. Likewise, the network can raise a signal when there is a new message. Although I would not
want to put that into the library for fear of adding a dependency.
so the finder needs to maintain state
- Waiting: Waiting for a finder request
- Sending: In the midst of looping through children
Needs to have some delay between each send. So the finder has a timer component.
Also... Reset should also reset the internal copy of the sync data. Sync may need a 'reset' method.

View File

@@ -0,0 +1,29 @@
/*
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
// Framework headers
// Library headers
#include <RF24.h>
#include <RF24Network.h>
// Project headers
// This component's header
#include <Globals.h>
#include "WProgram.h"
/****************************************************************************/
HardwareSPI SPI(2);
// Radio on Maple Native w/ Getting Started board
RF24 radio(7,6);
RF24Network network(radio);
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,36 @@
/*
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 __GLOBALS_H__
#define __GLOBALS_H__
// STL headers
// C headers
// Framework headers
// Library headers
// Project headers
class RF24;
class RF24Network;
extern RF24 radio;
extern RF24Network network;
/**
* Example for how classes should be declared
*/
class Template
{
private:
protected:
public:
};
#endif // __GLOBALS_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,12 @@
SubDir TOP ;
# Set up output directories
LOCATE_TARGET = $(SEARCH_SOURCE)/out/$(TOOLSET) ;
LOCATE_SOURCE = $(LOCATE_TARGET) ;
# Pull in local libraries
SKETCH_LIBS += RF24Network RF24 ;
HDRS += $(HOME)/Source/Arduino/libraries/$(SKETCH_LIBS) ;
# Main output executable
Maple $(SEARCH_SOURCE:B).elf : [ GLOB $(SEARCH_SOURCE) $(HOME)/Source/Arduino/libraries/$(SKETCH_LIBS) : $(MODULE_EXT) ] ;

View File

@@ -0,0 +1,251 @@
MCU = cortex-m3 ;
CHIP = STM32F103ZE ;
BOARD = maple_native ;
#CHIP = at91sam3u4 ;
#BOARD = sam3u-ek ;
if ! $(TOOLSET)
{
TOOLSET = mix ;
Echo "Assuming TOOLSET=mix" ;
}
else if $(TOOLSET) = cs
{
TOOLS_PATH = /opt/cs/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
else if $(TOOLSET) = yagarto
{
TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
if $(TOOLSET) = yagarto-install
{
TOOLS_PATH = ~/Source/yagarto/install/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
else if $(TOOLSET) = devkit
{
TOOLS_PATH = /opt/devkitARM/bin ;
TOOLS_ARCH = arm-eabi- ;
}
else if $(TOOLSET) = maple
{
TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
LINK_DIR = /opt/Maple/Resources/Java/hardware/leaflabs/cores/maple ;
MAPLE_DIR = /opt/Maple/Resources/Java/hardware/leaflabs/cores/maple ;
MAPLE_SUBDIRS = . ;
}
else if $(TOOLSET) = mix
{
TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ;
TOOLS_ARCH = arm-none-eabi- ;
LINKFLAGS += --print-gc-sections ;
}
else if $(TOOLSET) = ports
{
TOOLS_PATH = /opt/local/bin ;
TOOLS_ARCH = arm-none-eabi- ;
}
# Tool locations
CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ;
C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ;
AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ;
LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ;
OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ;
DFU = dfu-util ;
# Flags
DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ;
OPTIM = -Os ;
MFLAGS = cpu=$(MCU) thumb arch=armv7-m ;
CCFLAGS = -Wall -Wno-strict-aliasing -Wno-format -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ;
C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ;
LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ;
DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ;
# Directories
MAPLE_DIR ?= /opt/libmaple ;
MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ;
MAPLE_SUBDIRS ?= libmaple libmaple/usb libmaple/usb/usb_lib wirish wirish/comm wirish/boards ;
SKETCH_DIR = $(HOME)/Source/Maple ;
CORE_DIRS = $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS)/utility ;
HDRS = $(CORE_DIRS) ;
# Modules
MODULE_EXT = *.c *.cpp *.S *.pde *.ino *.test ;
CORE_MODULES = [ GLOB $(CORE_DIRS) : $(MODULE_EXT) ] ;
# Unit test framework
CXXTEST_DIR = $(HOME)/Source/cxxtest ;
CXXTEST_GEN = $(CXXTEST_DIR)/bin/cxxtestgen ;
HDRS += $(CXXTEST_DIR) ;
# Linker script
LINK_DIR ?= $(MAPLE_DIR)/support/ld ;
LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ;
LINKFLAGS += -T$(LINKSCRIPT) -L$(LINK_DIR) ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule TestSuite
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
actions TestSuite
{
$(CXXTEST_GEN) --part $(>) > $(<)
}
rule TestRoot
{
MakeLocate $(<) : $(LOCATE_SOURCE) ;
}
actions TestRoot
{
$(CXXTEST_GEN) --root --error-printer > $(<)
}
rule C++TestSuite
{
local _CPP = $(>:B).cpp ;
TestSuite $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex $(>) $(<)
}
rule Binary
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends binary : $(<) ;
Clean clean : $(<) ;
}
actions Binary
{
$(OBJCOPY) -O binary $(>) $(<)
}
rule UserObject
{
switch $(>:S)
{
case .S : As $(<) : $(>) ;
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
case .test : C++TestSuite $(<) : $(>) ;
}
}
rule Upload
{
Depends up : $(<) ;
NotFile up ;
Always $(<) ;
}
actions Upload
{
$(MAPLE_DIR)/support/scripts/reset.py
sleep 1
$(DFU) $(DFUFLAGS) -D $(<)
sleep 5
}
rule Login
{
MakeLocate $(<) : $(LOCATE_TARGET) ;
Always $(<) ;
}
actions Login
{
ls > $(<)
screen $(>)
}
# Override base objects rule, so all output can go in the output dir
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
# Override base main rule, so all output can go in the output dir
rule Main
{
# Bring in the map
LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ;
MakeLocate $(<) ;
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Maple
{
Main $(<) : $(>) $(CORE_MODULES) ;
Binary $(<:B).bin : $(<) ;
Upload $(<:B).bin ;
Login login : /dev/tty.usbmodemfa441 ;
}

View File

@@ -0,0 +1,145 @@
/*
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
#include <vector>
#include <string>
#include <iostream>
// C headers
#include <stdlib.h>
// Framework headers
// Library headers
#include <cxxtest/TestSuite.h>
// Project headers
#include <RF24Network.h>
#include <RF24.h>
// This component's header
#include <Sync.h>
#include "WProgram.h"
#include "Globals.h"
using namespace std;
class PingTestSuite: public CxxTest::TestSuite
{
public:
// 'Delay' but update the network for a bit
void net_delay(unsigned long amount)
{
unsigned long start = millis();
while ( millis() - start < amount )
{
network.update();
#if 0
// Is there anything ready for us?
if ( network.available() )
{
// If so, take a look at it
RF24NetworkHeader header;
network.read(header,0,0);
cout << millis() << ": net_delay got " << header.type << " from " << header.from_node << "\r\n";
}
#endif
}
}
void setUp()
{
// Reset remote to initial state
RF24NetworkHeader header(/*to node*/ 1, /*type*/ 'R' /*Reset*/);
network.write(header,0,0);
// Wait a bit for the message to take
net_delay(50);
}
void tearDown()
{
net_delay(100);
//cout << "\r\n Complete. Press any key \r\n";
//SerialUSB.read();
}
void testNothing()
{
cout << "\r\n STARTING " << __FUNCTION__ << "\r\n";
}
void testPing()
{
cout << "\r\n STARTING " << __FUNCTION__ << "\r\n";
// Send an echo ping to the other unit
RF24NetworkHeader header(/*to node*/ 1, /*type*/ 'E' /*Echo*/);
uint32_t testval = 0x12345678, gotval = 0;
network.write(header,&testval,sizeof(testval));
const unsigned long timeout = 1000;
unsigned long sent_at = millis();
while ( millis() - sent_at < timeout && ! gotval )
{
net_delay(100);
// 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 'E':
network.read(header,&gotval,sizeof(gotval));
break;
default:
// Anything else is unexpected, and ergo a test failure
network.read(header,0,0);
gotval = -1;
break;
};
}
}
TS_ASSERT_EQUALS( gotval, testval );
}
void testFinder()
{
cout << "\r\n STARTING " << __FUNCTION__ << "\r\n";
// Send a child finder to the other unit
RF24NetworkHeader header(/*to node*/ 1, /*type*/ 'F' /*Echo*/);
uint32_t testval = 0x12345678, gotval = 0;
network.write(header,&testval,sizeof(testval));
const unsigned long timeout = 3000;
unsigned long sent_at = millis();
while ( millis() - sent_at < timeout ) // && ! gotval )
{
network.update();
// Is there anything ready for us?
if ( network.available() )
{
// If so, take a look at it
RF24NetworkHeader header;
network.peek(header);
network.read(header,&gotval,sizeof(gotval));
cout << millis() << ": FINDER " << header.type << " from " << header.from_node << " " << hex << gotval << dec << "\r\n";
}
}
TS_ASSERT_EQUALS( gotval, testval );
}
};
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,12 @@
These unit tests only work on Maple. This test runs on a Maple,
as node #00. One other node is expected on node #01, running the
"unit_rx" sketch, which runs on Arduino.
In the future, it would be interesting to write a test which enumerated
the entire tree. Each node could implement a "child finder", where it
sends a message to each child. Upon receiving the message, each child
sends the message out to ITS children, AND sends a "Hello" to the base node.
This requires a 'F' finder request. For the response, we can re-use
the 'E' echo response. The 'E' echo system presumes that the base is
the requestor and the RX nodes are the responder.

View File

@@ -0,0 +1,123 @@
/*
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
#include <vector>
#include <string>
#include <iostream>
// C headers
#include <stdlib.h>
// Framework headers
// Library headers
#include <cxxtest/TestSuite.h>
// Project headers
#include <RF24Network.h>
#include <RF24.h>
// This component's header
#include <Sync.h>
#include "WProgram.h"
#include "Globals.h"
using namespace std;
class SyncTestSuite: public CxxTest::TestSuite
{
struct sync_data_t
{
uint16_t first;
uint16_t second;
sync_data_t(void): first(1), second(2) {}
};
sync_data_t* p_sync_data;
Sync* pSync;
public:
// 'Delay' but update the network for a bit
void net_delay(unsigned long amount)
{
unsigned long start = millis();
while ( millis() - start < amount )
{
pSync->update();
// Is there anything ready for us?
if ( network.available() )
{
// If so, take a look at it
RF24NetworkHeader header;
network.read(header,0,0);
cout << millis() << ": net_delay got " << header.type << " from " << header.from_node << "\r\n";
}
}
}
void setUp()
{
pSync = new Sync(network);
pSync->begin(/* other node*/ 1);
p_sync_data = new(sync_data_t);
// Reset remote to initial state
RF24NetworkHeader header(/*to node*/ 1, /*type*/ 'R' /*Reset*/);
network.write(header,0,0);
// Wait a bit for the message to take
net_delay(200);
}
void tearDown()
{
net_delay(100);
//cout << "\r\n Complete. Press any key \r\n";
//SerialUSB.read();
delete p_sync_data;
delete pSync;
}
void testNoUpdate( void )
{
cout << "\r\n STARTING " << __FUNCTION__ << "\r\n";
pSync->register_me(*p_sync_data);
int i = 10;
while (i--)
pSync->update();
TS_ASSERT_EQUALS(p_sync_data->first,1);
}
void testSync( void )
{
cout << "\r\n STARTING " << __FUNCTION__ << "\r\n";
pSync->register_me(*p_sync_data);
int i = 10;
while (i--)
pSync->update();
const uint16_t testval = 10;
p_sync_data->first = testval;
// wait a little while. During this time, the 'first' value will be propagated
// out to the other unit, it will set the value onto 'second', and it should get
// propagated back.
const unsigned long interval = 1000;
unsigned long sent_at = millis();
while ( millis() - sent_at < interval && p_sync_data->second != testval )
pSync->update();
TS_ASSERT_EQUALS(p_sync_data->second,testval);
}
};
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,77 @@
#ifdef MAPLE_IDE
#include <stdio.h>
#include "wirish.h"
extern void setup(void);
extern void loop(void);
void board_start(const char* program_name)
{
// Set up the LED to steady on
pinMode(BOARD_LED_PIN, OUTPUT);
digitalWrite(BOARD_LED_PIN, HIGH);
// Setup the button as input
pinMode(BOARD_BUTTON_PIN, INPUT);
digitalWrite(BOARD_BUTTON_PIN, HIGH);
SerialUSB.begin();
SerialUSB.println("Press BUT");
// Wait for button press
while ( !isButtonPressed() )
{
}
SerialUSB.println("Welcome!");
SerialUSB.println(program_name);
int i = 11;
while (i--)
{
toggleLED();
delay(50);
}
}
/**
* Custom version of _write, which will print to the USB.
* In order to use it you MUST ADD __attribute__((weak))
* to _write in libmaple/syscalls.c
*/
extern "C" int _write(int file, char * ptr, int len)
{
if ( (file != 1) && (file != 2) )
return 0;
else
SerialUSB.write(ptr,len);
return len;
}
__attribute__((constructor)) __attribute__ ((weak)) void premain()
{
init();
}
__attribute__((weak)) void setup(void)
{
board_start("No program defined");
}
__attribute__((weak)) void loop(void)
{
}
__attribute__((weak)) int main(void)
{
setup();
while (true)
{
loop();
}
return 0;
}
#endif // ifdef MAPLE_IDE
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,43 @@
#include <stdio.h>
#ifndef CXXTEST_RUNNING
#define CXXTEST_RUNNING
#endif
#define _CXXTEST_HAVE_STD
#define _CXXTEST_LONGLONG long long
#include <cxxtest/TestListener.h>
#include <cxxtest/TestTracker.h>
#include <cxxtest/TestRunner.h>
#include <cxxtest/RealDescriptions.h>
#include <cxxtest/TestMain.h>
#include <cxxtest/ErrorPrinter.h>
#include <cxxtest/Root.cpp>
#include <RF24Network_config.h>
#include <RF24Network.h>
#include <RF24.h>
extern void board_start(const char*);
extern RF24Network network;
extern RF24 radio;
int main( void )
{
CxxTest::ErrorPrinter tmp;
while(1)
{
board_start(__FILE__);
SPI.begin();
radio.begin();
network.begin(/* channel */100,/* this node */0);
CxxTest::Main<CxxTest::ErrorPrinter>( tmp, 0, NULL );
printf("Tests complete. Restarting...\r\n");
}
}
const char* CxxTest::RealWorldDescription::_worldName = "cxxtest";
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,129 @@
/*
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
// Framework headers
// Library headers
#include "RF24Network.h"
// Project headers
// This component's header
#include <Finder.h>
extern RF24Network network;
// Message buffer space
static uint8_t message[32];
/****************************************************************************/
Finder::Finder(void): this_node(0), state(state_waiting),
last_sent(millis()-interval), child_increment(-1)
{
}
/****************************************************************************/
void Finder::begin(uint16_t _this_node)
{
this_node = _this_node;
if ( ! ( this_node & 07000 ) )
{
// Figure out the address of the first child. e.g. if our node is 045, our
// first child is 0145. So we need to shift 01 up enough places to be the
// highest digit
child_increment = 01;
uint16_t temp = this_node;
while ( temp )
{
child_increment <<= 3;
temp >>= 3;
}
}
printf_P(PSTR("%lu: Node %o increment %o\r\n"),millis(),this_node,child_increment);
}
/****************************************************************************/
void Finder::update(void)
{
// Check the network for traffic
// If we got a new finder request, launch!
if ( network.available() )
{
RF24NetworkHeader header;
network.peek(header);
if ( header.type == 'F' )
{
network.read(header,message,sizeof(message));
uint16_t from = header.from_node;
printf_P(PSTR("%lu: APP Received FINDER request from %o\r\n"),millis(),from);
if ( state == state_sending )
{
printf_P(PSTR("%lu: APP ERROR, already sending a finder request\r\n"),millis());
}
else if ( child_increment == 0xffff )
{
printf_P(PSTR("%lu: APP This app has no children, done.\r\n"),millis());
last_sent = millis() - interval;
state = state_done;
goto finish;
}
else
{
last_sent = millis() - interval;
to_node = this_node + child_increment;
state = state_sending;
goto finish;
}
}
}
// If we're working but not ready, continue
if ( state != state_waiting && millis() - last_sent <= interval )
return;
// If we're working, send!
if ( state == state_sending )
{
RF24NetworkHeader header(to_node,'F');
/*bool ok = */ network.write(header,message,sizeof(message));
last_sent = millis();
to_node += child_increment;
// Done?
if ( to_node > this_node + 5 * child_increment )
{
state = state_done;
goto finish;
}
}
// If we're now done, send the final 'E' back
if ( state == state_done )
{
// Send an 'E' Echo response back to the BASE
RF24NetworkHeader header(00,'E');
network.write(header,message,sizeof(message));
state = state_waiting;
goto finish;
}
finish:
return;
}
/****************************************************************************/
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,42 @@
/*
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 __FINDER_H__
#define __FINDER_H__
// STL headers
// C headers
// Framework headers
#include <Arduino.h>
// Library headers
// Project headers
/**
* Manage a child-finder request
*/
class Finder
{
private:
uint16_t this_node;
enum state_e { state_none = 0, state_waiting, state_sending, state_done, state_invalid };
state_e state;
static const unsigned long interval = 50;
unsigned long last_sent;
uint16_t to_node;
uint16_t child_increment;
protected:
public:
Finder(void);
void begin(uint16_t);
void update(void);
};
#endif // __FINDER_H__
// vim:cin:ai:sts=2 sw=2 ft=cpp

View File

@@ -0,0 +1,210 @@
# (1) Project Information
PROJECT_LIBS = SPI RF24 RF24Network ;
# (2) Board Information
UPLOAD_PROTOCOL ?= stk500v1 ;
UPLOAD_SPEED ?= 57600 ;
MCU ?= atmega328p ;
F_CPU ?= 16000000 ;
CORE ?= arduino ;
VARIANT ?= standard ;
ARDUINO_VERSION ?= 100 ;
# (3) USB Ports
PORTS = p4 p6 p9 u0 u1 u2 ;
PORT_p6 = /dev/tty.usbserial-A600eHIs ;
PORT_p4 = /dev/tty.usbserial-A40081RP ;
PORT_p9 = /dev/tty.usbserial-A9007LmI ;
PORT_u0 = /dev/ttyUSB0 ;
PORT_u1 = /dev/ttyUSB1 ;
PORT_u2 = /dev/ttyUSB2 ;
# (4) Location of AVR tools
#
# This configuration assumes using avr-tools that were obtained separate from the Arduino
# distribution.
if $(OS) = MACOSX
{
AVR_BIN = /usr/local/avrtools/bin ;
AVR_ETC = /usr/local/avrtools/etc ;
AVR_INCLUDE = /usr/local/avrtools/include ;
}
else
{
AVR_BIN = /usr/bin ;
AVR_INCLUDE = /usr/lib/avr/include ;
AVR_ETC = /etc ;
}
# (5) Directories where Arduino core and libraries are located
ARDUINO_DIR ?= /opt/Arduino ;
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ;
ARDUINO_LIB = $(ARDUINO_DIR)/libraries ;
SKETCH_LIB = $(HOME)/Source/Arduino/libraries ;
#
# --------------------------------------------------
# Below this line usually never needs to be modified
#
# Tool locations
CC = $(AVR_BIN)/avr-gcc ;
C++ = $(AVR_BIN)/avr-g++ ;
LINK = $(AVR_BIN)/avr-gcc ;
OBJCOPY = $(AVR_BIN)/avr-objcopy ;
AVRDUDE = $(AVR_BIN)/avrdude ;
# Flags
DEFINES += NODE=$(NODE) F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ;
OPTIM = -Os ;
CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ;
C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ;
LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ;
AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ;
# Search everywhere for headers
HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ;
# Output locations
LOCATE_TARGET = $(F_CPU) ;
LOCATE_SOURCE = $(F_CPU) ;
#
# Custom rules
#
rule GitVersion
{
Always $(<) ;
Depends all : $(<) ;
}
actions GitVersion
{
echo "const char program_version[] = \"\\" > $(<)
git log -1 --pretty=format:%h >> $(<)
echo "\";" >> $(<)
}
GitVersion version.h ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule UserObject
{
switch $(>:S)
{
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
}
}
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
rule Main
{
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex -R .eeprom $(>) $(<)
}
rule Upload
{
Depends $(1) : $(2) ;
Depends $(2) : $(3) ;
NotFile $(1) ;
Always $(1) ;
Always $(2) ;
UploadAction $(2) : $(3) ;
}
actions UploadAction
{
$(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i
}
#
# Targets
#
# Grab everything from the core directory
CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ;
# Grab everything from libraries. To avoid this "grab everything" behaviour, you
# can specify specific modules to pick up in PROJECT_MODULES
LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ;
# Grab everything from the current dir
PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ;
# Main output executable
MAIN = $(PWD:B).elf ;
Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ;
Hex $(MAIN:B).hex : $(MAIN) ;
# Upload targets
for _p in $(PORTS)
{
Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ;
}

View File

@@ -0,0 +1,19 @@
This is the 'receiver' for the unit tests. Run it on a node which
is NOT under test.
TODO
Send finder request needs a re-think. It needs to be re-implemented as a Tictocs::Timer, so it
doesn't block the loop().
I could also make Tictocs objects which monitor the RF24network and pull off messages that are just
for them. Likewise, the network can raise a signal when there is a new message. Although I would not
want to put that into the library for fear of adding a dependency.
so the finder needs to maintain state
- Waiting: Waiting for a finder request
- Sending: In the midst of looping through children
Needs to have some delay between each send. So the finder has a timer component.
Also... Reset should also reset the internal copy of the sync data. Sync may need a 'reset' method.

View File

@@ -0,0 +1,31 @@
/*
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.
*/
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#endif // __PRINTF_H__

View File

@@ -0,0 +1,135 @@
/*
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.
*/
/**
* Receiver for unit tests.
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Sync.h>
#include "Finder.h"
#include "printf.h"
// nRF24L01(+) radio attached to SPI and pins 8 & 9
RF24 radio(8,9);
// Network uses that radio
RF24Network network(radio);
// Syncronizer
Sync sync(network);
// Address of our node
#if NODE > 0
const uint16_t this_node = NODE;
#else
const uint16_t this_node = 1;
#endif
// Address of the other node
const uint16_t other_node = 0;
// Data to be synchronized
struct sync_data_t
{
uint16_t first;
uint16_t second;
sync_data_t(void): first(1), second(2) {}
};
sync_data_t sync_data;
// Old value of 'first'
uint16_t old_first;
// Message buffer space
uint8_t message[32];
void send_finder_request(void);
void net_delay(unsigned long amount);
Finder finder;
void setup(void)
{
Serial.begin(57600);
Serial.println("RF24Network/test/unit_rx/");
printf_begin();
SPI.begin();
radio.begin();
network.begin(/*channel*/ 100, /*node address*/ this_node);
finder.begin(this_node);
sync.register_me(sync_data);
old_first = sync_data.first;
}
void loop(void)
{
// Pump the network regularly
sync.update();
// Stay on the lookout for finder messages
finder.update();
// Have the values changed?
if ( old_first != sync_data.first )
{
printf_P(PSTR("%lu: APP Updated first to %u\n\r"),millis(),sync_data.first);
// Move the first value over to the second
sync_data.second = sync_data.first;
// And remember the change for next time
old_first = sync_data.first;
}
// Are there any messages for us?
while ( network.available() )
{
uint16_t from;
// If so, take a look at it
RF24NetworkHeader header;
network.peek(header);
// Dispatch the message to the correct handler.
switch (header.type)
{
// Reset to initial state
case 'R':
network.read(header,0,0);
printf_P(PSTR("%lu: APP Reset to initial state\n\r"),millis());
sync_data = sync_data_t();
old_first = sync_data.first;
sync.reset();
break;
// Echo back to the sender.
case 'E':
network.read(header,message,sizeof(message));
from = header.from_node;
printf_P(PSTR("%lu: APP Received ECHO request from %o\n\r"),millis(),from);
network.write(header = RF24NetworkHeader(from,'E'),message,sizeof(message));
break;
// Unrecognized message type
default:
printf_P(PSTR("*** WARNING *** Unknown message type %c\n\r"),header.type);
network.read(header,0,0);
break;
};
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp