Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:ab4e012489ef, committed 2015-10-15
- Comitter:
- davidr99
- Date:
- Thu Oct 15 01:27:00 2015 +0000
- Commit message:
- Messy start, but a port for RadioHead.; Currently the SPI modulus are the only ones that work.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHCRC.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,104 @@
+/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz
+ Copyright (c) 2005, 2007 Joerg Wunsch
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of the copyright holders nor the names of
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE. */
+
+// Port to Energia / MPS430 by Yannick DEVOS XV4Y - (c) 2013
+// http://xv4y.radioclub.asia/
+//
+
+// Adapted to RadioHead use by Mike McCauley 2014
+// This is to prevent name collisions with other similar library functions
+// and to provide a consistent API amonng all processors
+//
+
+/* $Id: RHCRC.cpp,v 1.1 2014/06/24 02:40:12 mikem Exp $ */
+
+#include <RHCRC.h>
+
+#define lo8(x) ((x)&0xff)
+#define hi8(x) ((x)>>8)
+
+uint16_t RHcrc16_update(uint16_t crc, uint8_t a)
+{
+ int i;
+
+ crc ^= a;
+ for (i = 0; i < 8; ++i)
+ {
+ if (crc & 1)
+ crc = (crc >> 1) ^ 0xA001;
+ else
+ crc = (crc >> 1);
+ }
+ return crc;
+}
+
+uint16_t RHcrc_xmodem_update (uint16_t crc, uint8_t data)
+{
+ int i;
+
+ crc = crc ^ ((uint16_t)data << 8);
+ for (i=0; i<8; i++)
+ {
+ if (crc & 0x8000)
+ crc = (crc << 1) ^ 0x1021;
+ else
+ crc <<= 1;
+ }
+
+ return crc;
+}
+
+uint16_t RHcrc_ccitt_update (uint16_t crc, uint8_t data)
+{
+ data ^= lo8 (crc);
+ data ^= data << 4;
+
+ return ((((uint16_t)data << 8) | hi8 (crc)) ^ (uint8_t)(data >> 4)
+ ^ ((uint16_t)data << 3));
+}
+
+uint8_t RHcrc_ibutton_update(uint8_t crc, uint8_t data)
+{
+ uint8_t i;
+
+ crc = crc ^ data;
+ for (i = 0; i < 8; i++)
+ {
+ if (crc & 0x01)
+ crc = (crc >> 1) ^ 0x8C;
+ else
+ crc >>= 1;
+ }
+
+ return crc;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RHCRC.h Thu Oct 15 01:27:00 2015 +0000 @@ -0,0 +1,19 @@ +// RHCRC.h +// +// Definitions for RadioHead compatible CRC outines. +// +// These routines originally derived from Arduino source code. See RHCRC.cpp +// for copyright information +// $Id: RHCRC.h,v 1.1 2014/06/24 02:40:12 mikem Exp $ + +#ifndef RHCRC_h +#define RHCRC_h + +#include <RadioHead.h> + +extern uint16_t RHcrc16_update(uint16_t crc, uint8_t a); +extern uint16_t RHcrc_xmodem_update (uint16_t crc, uint8_t data); +extern uint16_t RHcrc_ccitt_update (uint16_t crc, uint8_t data); +extern uint8_t RHcrc_ibutton_update(uint8_t crc, uint8_t data); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHDatagram.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,123 @@
+// RHDatagram.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHDatagram.cpp,v 1.6 2014/05/23 02:20:17 mikem Exp $
+
+#include <RHDatagram.h>
+
+RHDatagram::RHDatagram(RHGenericDriver& driver, uint8_t thisAddress)
+ :
+ _driver(driver),
+ _thisAddress(thisAddress)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+bool RHDatagram::init()
+{
+ bool ret = _driver.init();
+ if (ret)
+ setThisAddress(_thisAddress);
+ return ret;
+}
+
+void RHDatagram::setThisAddress(uint8_t thisAddress)
+{
+ _driver.setThisAddress(thisAddress);
+ // Use this address in the transmitted FROM header
+ setHeaderFrom(thisAddress);
+ _thisAddress = thisAddress;
+}
+
+bool RHDatagram::sendto(uint8_t* buf, uint8_t len, uint8_t address)
+{
+ setHeaderTo(address);
+ return _driver.send(buf, len);
+}
+
+bool RHDatagram::recvfrom(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{
+ if (_driver.recv(buf, len))
+ {
+ if (from) *from = headerFrom();
+ if (to) *to = headerTo();
+ if (id) *id = headerId();
+ if (flags) *flags = headerFlags();
+ return true;
+ }
+ return false;
+}
+
+bool RHDatagram::available()
+{
+ return _driver.available();
+}
+
+void RHDatagram::waitAvailable()
+{
+ _driver.waitAvailable();
+}
+
+bool RHDatagram::waitPacketSent()
+{
+ return _driver.waitPacketSent();
+}
+
+bool RHDatagram::waitPacketSent(uint16_t timeout)
+{
+ return _driver.waitPacketSent(timeout);
+}
+
+bool RHDatagram::waitAvailableTimeout(uint16_t timeout)
+{
+ return _driver.waitAvailableTimeout(timeout);
+}
+
+uint8_t RHDatagram::thisAddress()
+{
+ return _thisAddress;
+}
+
+void RHDatagram::setHeaderTo(uint8_t to)
+{
+ _driver.setHeaderTo(to);
+}
+
+void RHDatagram::setHeaderFrom(uint8_t from)
+{
+ _driver.setHeaderFrom(from);
+}
+
+void RHDatagram::setHeaderId(uint8_t id)
+{
+ _driver.setHeaderId(id);
+}
+
+void RHDatagram::setHeaderFlags(uint8_t set, uint8_t clear)
+{
+ _driver.setHeaderFlags(set, clear);
+}
+
+uint8_t RHDatagram::headerTo()
+{
+ return _driver.headerTo();
+}
+
+uint8_t RHDatagram::headerFrom()
+{
+ return _driver.headerFrom();
+}
+
+uint8_t RHDatagram::headerId()
+{
+ return _driver.headerId();
+}
+
+uint8_t RHDatagram::headerFlags()
+{
+ return _driver.headerFlags();
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHDatagram.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,162 @@
+// RHDatagram.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHDatagram.h,v 1.14 2015/08/12 23:18:51 mikem Exp $
+
+#ifndef RHDatagram_h
+#define RHDatagram_h
+
+#include <RHGenericDriver.h>
+
+// This is the maximum possible message size for radios supported by RadioHead.
+// Not all radios support this length, and many are much smaller
+#define RH_MAX_MESSAGE_LEN 255
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHDatagram RHDatagram.h <RHDatagram.h>
+/// \brief Manager class for addressed, unreliable messages
+///
+/// Every RHDatagram node has an 8 bit address (defaults to 0).
+/// Addresses (DEST and SRC) are 8 bit integers with an address of RH_BROADCAST_ADDRESS (0xff)
+/// reserved for broadcast.
+///
+/// \par Media Access Strategy
+///
+/// RHDatagram and the underlying drivers always transmit as soon as sendto() is called.
+///
+/// \par Message Lengths
+///
+/// Not all Radio drivers supported by RadioHead can handle the same message lengths. Some radios can handle
+/// up to 255 octets, and some as few as 28. If you attempt to send a message that is too long for
+/// the underlying driver, sendTo() will return false and will not transmit the message.
+/// It is the programmers responsibility to make
+/// sure that messages passed to sendto() do not exceed the capability of the radio. You can use the
+/// *_MAX_MESSAGE_LENGTH definitions or driver->maxMessageLength() to help.
+///
+/// \par Headers
+///
+/// Each message sent and received by a RadioHead driver includes 4 headers:<br>
+/// \b TO The node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted)<br>
+/// \b FROM The node address of the sending node<br>
+/// \b ID A message ID, distinct (over short time scales) for each message sent by a particilar node<br>
+/// \b FLAGS A bitmask of flags. The most significant 4 bits are reserved for use by RadioHead. The least
+/// significant 4 bits are reserved for applications.<br>
+///
+class RHDatagram
+{
+public:
+ /// Constructor.
+ /// \param[in] driver The RadioHead driver to use to transport messages.
+ /// \param[in] thisAddress The address to assign to this node. Defaults to 0
+ RHDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0);
+
+ /// Initialise this instance and the
+ /// driver connected to it.
+ bool init();
+
+ /// Sets the address of this node. Defaults to 0.
+ /// This will be used to set the FROM address of all messages sent by this node.
+ /// In a conventional multinode system, all nodes will have a unique address
+ /// (which you could store in EEPROM).
+ /// \param[in] thisAddress The address of this node
+ void setThisAddress(uint8_t thisAddress);
+
+ /// Sends a message to the node(s) with the given address
+ /// RH_BROADCAST_ADDRESS is a valid address which will cause the message
+ /// to be accepted by all RHDatagram nodes within range.
+ /// \param[in] buf Pointer to the binary message to send
+ /// \param[in] len Number of octets to send (> 0)
+ /// \param[in] address The address to send the message to.
+ /// \return true if the message not too loing fot eh driver, and the message was transmitted.
+ bool sendto(uint8_t* buf, uint8_t len, uint8_t address);
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available for this node, copy it to buf and return true
+ /// The SRC address is placed in *from if present and not NULL.
+ /// The DEST address is placed in *to if present and not NULL.
+ /// If a message is copied, *len is set to the length.
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the FROM address
+ /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the TO address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was copied to buf
+ bool recvfrom(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+ /// Tests whether a new message is available
+ /// from the Driver.
+ /// On most drivers, this will also put the Driver into RHModeRx mode until
+ /// a message is actually received bythe transport, when it will be returned to RHModeIdle.
+ /// This can be called multiple times in a timeout loop.
+ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
+ bool available();
+
+ /// Starts the Driver receiver and blocks until a valid received
+ /// message is available.
+ void waitAvailable();
+
+ /// Blocks until the transmitter
+ /// is no longer transmitting.
+ bool waitPacketSent();
+
+ /// Blocks until the transmitter is no longer transmitting.
+ /// or until the timeout occuers, whichever happens first
+ /// \param[in] timeout Maximum time to wait in milliseconds.
+ /// \return true if the radio completed transmission within the timeout period. False if it timed out.
+ bool waitPacketSent(uint16_t timeout);
+
+ /// Starts the Driver receiver and blocks until a received message is available or a timeout
+ /// \param[in] timeout Maximum time to wait in milliseconds.
+ /// \return true if a message is available
+ bool waitAvailableTimeout(uint16_t timeout);
+
+ /// Sets the TO header to be sent in all subsequent messages
+ /// \param[in] to The new TO header value
+ void setHeaderTo(uint8_t to);
+
+ /// Sets the FROM header to be sent in all subsequent messages
+ /// \param[in] from The new FROM header value
+ void setHeaderFrom(uint8_t from);
+
+ /// Sets the ID header to be sent in all subsequent messages
+ /// \param[in] id The new ID header value
+ void setHeaderId(uint8_t id);
+
+ /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages
+ /// \param[in] set bitmask of bits to be set
+ /// \param[in] clear bitmask of flags to clear
+ void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_NONE);
+
+ /// Returns the TO header of the last received message
+ /// \return The TO header of the most recently received message.
+ uint8_t headerTo();
+
+ /// Returns the FROM header of the last received message
+ /// \return The FROM header of the most recently received message.
+ uint8_t headerFrom();
+
+ /// Returns the ID header of the last received message
+ /// \return The ID header of the most recently received message.
+ uint8_t headerId();
+
+ /// Returns the FLAGS header of the last received message
+ /// \return The FLAGS header of the most recently received message.
+ uint8_t headerFlags();
+
+ /// Returns the address of this node.
+ /// \return The address of this node
+ uint8_t thisAddress();
+
+protected:
+ /// The Driver we are to use
+ RHGenericDriver& _driver;
+
+ /// The address of this node
+ uint8_t _thisAddress;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericDriver.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,187 @@
+// RHGenericDriver.cpp
+//
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHGenericDriver.cpp,v 1.18 2015/01/02 21:38:24 mikem Exp $
+
+#include <RHGenericDriver.h>
+
+Timer _millisTimer;
+
+RHGenericDriver::RHGenericDriver()
+ :
+ _mode(RHModeInitialising),
+ _thisAddress(RH_BROADCAST_ADDRESS),
+ _txHeaderTo(RH_BROADCAST_ADDRESS),
+ _txHeaderFrom(RH_BROADCAST_ADDRESS),
+ _txHeaderId(0),
+ _txHeaderFlags(0),
+ _rxBad(0),
+ _rxGood(0),
+ _txGood(0)
+{
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ _millisTimer.start();
+#endif
+}
+
+bool RHGenericDriver::init()
+{
+ return true;
+}
+
+// Blocks until a valid message is received
+void RHGenericDriver::waitAvailable()
+{
+ while (!available())
+ YIELD;
+}
+
+// Blocks until a valid message is received or timeout expires
+// Return true if there is a message available
+// Works correctly even on millis() rollover
+bool RHGenericDriver::waitAvailableTimeout(uint16_t timeout)
+{
+ unsigned long starttime = millis();
+ while ((millis() - starttime) < timeout)
+ {
+ if (available())
+ return true;
+ YIELD;
+ }
+ return false;
+}
+
+bool RHGenericDriver::waitPacketSent()
+{
+ while (_mode == RHModeTx)
+ YIELD; // Wait for any previous transmit to finish
+ return true;
+}
+
+bool RHGenericDriver::waitPacketSent(uint16_t timeout)
+{
+ unsigned long starttime = millis();
+ while ((millis() - starttime) < timeout)
+ {
+ if (_mode != RHModeTx) // Any previous transmit finished?
+ return true;
+ YIELD;
+ }
+ return false;
+}
+
+void RHGenericDriver::setPromiscuous(bool promiscuous)
+{
+ _promiscuous = promiscuous;
+}
+
+void RHGenericDriver::setThisAddress(uint8_t address)
+{
+ _thisAddress = address;
+}
+
+void RHGenericDriver::setHeaderTo(uint8_t to)
+{
+ _txHeaderTo = to;
+}
+
+void RHGenericDriver::setHeaderFrom(uint8_t from)
+{
+ _txHeaderFrom = from;
+}
+
+void RHGenericDriver::setHeaderId(uint8_t id)
+{
+ _txHeaderId = id;
+}
+
+void RHGenericDriver::setHeaderFlags(uint8_t set, uint8_t clear)
+{
+ _txHeaderFlags &= ~clear;
+ _txHeaderFlags |= set;
+}
+
+uint8_t RHGenericDriver::headerTo()
+{
+ return _rxHeaderTo;
+}
+
+uint8_t RHGenericDriver::headerFrom()
+{
+ return _rxHeaderFrom;
+}
+
+uint8_t RHGenericDriver::headerId()
+{
+ return _rxHeaderId;
+}
+
+uint8_t RHGenericDriver::headerFlags()
+{
+ return _rxHeaderFlags;
+}
+
+int8_t RHGenericDriver::lastRssi()
+{
+ return _lastRssi;
+}
+
+RHGenericDriver::RHMode RHGenericDriver::mode()
+{
+ return _mode;
+}
+
+void RHGenericDriver::setMode(RHMode mode)
+{
+ _mode = mode;
+}
+
+bool RHGenericDriver::sleep()
+{
+ return false;
+}
+
+// Diagnostic help
+void RHGenericDriver::printBuffer(const char* prompt, const uint8_t* buf, uint8_t len)
+{
+ uint8_t i;
+
+#ifdef RH_HAVE_SERIAL
+ Serial.println(prompt);
+ for (i = 0; i < len; i++)
+ {
+ if (i % 16 == 15)
+ Serial.println(buf[i], HEX);
+ else
+ {
+ Serial.print(buf[i], HEX);
+ Serial.print(' ');
+ }
+ }
+ Serial.println("");
+#endif
+}
+
+uint16_t RHGenericDriver::rxBad()
+{
+ return _rxBad;
+}
+
+uint16_t RHGenericDriver::rxGood()
+{
+ return _rxGood;
+}
+
+uint16_t RHGenericDriver::txGood()
+{
+ return _txGood;
+}
+
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(RH_PLATFORM_ATTINY)
+// Tinycore does not have __cxa_pure_virtual, so without this we
+// get linking complaints from the default code generated for pure virtual functions
+extern "C" void __cxa_pure_virtual()
+{
+ while (1);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericDriver.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,265 @@
+// RHGenericDriver.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHGenericDriver.h,v 1.16 2015/01/02 21:38:24 mikem Exp $
+
+#ifndef RHGenericDriver_h
+#define RHGenericDriver_h
+
+#include <RadioHead.h>
+
+// Defines bits of the FLAGS header reserved for use by the RadioHead library and
+// the flags available for use by applications
+#define RH_FLAGS_RESERVED 0xf0
+#define RH_FLAGS_APPLICATION_SPECIFIC 0x0f
+#define RH_FLAGS_NONE 0
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHGenericDriver RHGenericDriver.h <RHGenericDriver.h>
+/// \brief Abstract base class for a RadioHead driver.
+///
+/// This class defines the functions that must be provided by any RadioHead driver.
+/// Different types of driver will implement all the abstract functions, and will perhaps override
+/// other functions in this subclass, or perhaps add new functions specifically required by that driver.
+/// Do not directly instantiate this class: it is only to be subclassed by driver classes.
+///
+/// Subclasses are expected to implement a half-duplex, unreliable, error checked, unaddressed packet transport.
+/// They are expected to carry a message payload with an appropriate maximum length for the transport hardware
+/// and to also carry unaltered 4 message headers: TO, FROM, ID, FLAGS
+///
+/// \par Headers
+///
+/// Each message sent and received by a RadioHead driver includes 4 headers:
+/// -TO The node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted)
+/// -FROM The node address of the sending node
+/// -ID A message ID, distinct (over short time scales) for each message sent by a particilar node
+/// -FLAGS A bitmask of flags. The most significant 4 bits are reserved for use by RadioHead. The least
+/// significant 4 bits are reserved for applications.
+class RHGenericDriver
+{
+public:
+ /// \brief Defines different operating modes for the transport hardware
+ ///
+ /// These are the different values that can be adopted by the _mode variable and
+ /// returned by the mode() member function,
+ typedef enum
+ {
+ RHModeInitialising = 0, ///< Transport is initialising. Initial default value until init() is called..
+ RHModeSleep, ///< Transport hardware is in low power sleep mode (if supported)
+ RHModeIdle, ///< Transport is idle.
+ RHModeTx, ///< Transport is in the process of transmitting a message.
+ RHModeRx ///< Transport is in the process of receiving a message.
+ } RHMode;
+
+ /// Constructor
+ RHGenericDriver();
+
+ /// Initialise the Driver transport hardware and software.
+ /// Make sure the Driver is properly configured before calling init().
+ /// \return true if initialisation succeeded.
+ virtual bool init();
+
+ /// Tests whether a new message is available
+ /// from the Driver.
+ /// On most drivers, if there is an uncollected received message, and there is no message
+ /// currently bing transmitted, this will also put the Driver into RHModeRx mode until
+ /// a message is actually received by the transport, when it will be returned to RHModeIdle.
+ /// This can be called multiple times in a timeout loop.
+ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv().
+ virtual bool available() = 0;
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ virtual bool recv(uint8_t* buf, uint8_t* len) = 0;
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will
+ /// return false and will not send the message.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ virtual bool send(const uint8_t* data, uint8_t len) = 0;
+
+ /// Returns the maximum message length
+ /// available in this Driver.
+ /// \return The maximum legal message length
+ virtual uint8_t maxMessageLength() = 0;
+
+ /// Starts the receiver and blocks until a valid received
+ /// message is available.
+ virtual void waitAvailable();
+
+ /// Blocks until the transmitter
+ /// is no longer transmitting.
+ virtual bool waitPacketSent();
+
+ /// Blocks until the transmitter is no longer transmitting.
+ /// or until the timeout occuers, whichever happens first
+ /// \param[in] timeout Maximum time to wait in milliseconds.
+ /// \return true if the RF22 completed transmission within the timeout period. False if it timed out.
+ virtual bool waitPacketSent(uint16_t timeout);
+
+ /// Starts the receiver and blocks until a received message is available or a timeout
+ /// \param[in] timeout Maximum time to wait in milliseconds.
+ /// \return true if a message is available
+ virtual bool waitAvailableTimeout(uint16_t timeout);
+
+ /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this.
+ /// This will be used to test the adddress in incoming messages. In non-promiscuous mode,
+ /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted.
+ /// In promiscuous mode, all messages will be accepted regardless of the TO header.
+ /// In a conventional multinode system, all nodes will have a unique address
+ /// (which you could store in EEPROM).
+ /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to,
+ /// allowing the possibilty of address spoofing).
+ /// \param[in] thisAddress The address of this node.
+ virtual void setThisAddress(uint8_t thisAddress);
+
+ /// Sets the TO header to be sent in all subsequent messages
+ /// \param[in] to The new TO header value
+ virtual void setHeaderTo(uint8_t to);
+
+ /// Sets the FROM header to be sent in all subsequent messages
+ /// \param[in] from The new FROM header value
+ virtual void setHeaderFrom(uint8_t from);
+
+ /// Sets the ID header to be sent in all subsequent messages
+ /// \param[in] id The new ID header value
+ virtual void setHeaderId(uint8_t id);
+
+ /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages
+ /// First it clears he FLAGS according to the clear argument, then sets the flags according to the
+ /// set argument. The default for clear always clears the application specific flags.
+ /// \param[in] set bitmask of bits to be set. Flags are cleared with the clear mask before being set.
+ /// \param[in] clear bitmask of flags to clear. Defaults to RH_FLAGS_APPLICATION_SPECIFIC
+ /// which clears the application specific flags, resultiung in new application specific flags
+ /// identical to the set.
+ virtual void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_APPLICATION_SPECIFIC);
+
+ /// Tells the receiver to accept messages with any TO address, not just messages
+ /// addressed to thisAddress or the broadcast address
+ /// \param[in] promiscuous true if you wish to receive messages with any TO address
+ virtual void setPromiscuous(bool promiscuous);
+
+ /// Returns the TO header of the last received message
+ /// \return The TO header
+ virtual uint8_t headerTo();
+
+ /// Returns the FROM header of the last received message
+ /// \return The FROM header
+ virtual uint8_t headerFrom();
+
+ /// Returns the ID header of the last received message
+ /// \return The ID header
+ virtual uint8_t headerId();
+
+ /// Returns the FLAGS header of the last received message
+ /// \return The FLAGS header
+ virtual uint8_t headerFlags();
+
+ /// Returns the most recent RSSI (Receiver Signal Strength Indicator).
+ /// Usually it is the RSSI of the last received message, which is measured when the preamble is received.
+ /// If you called readRssi() more recently, it will return that more recent value.
+ /// \return The most recent RSSI measurement in dBm.
+ int8_t lastRssi();
+
+ /// Returns the operating mode of the library.
+ /// \return the current mode, one of RF69_MODE_*
+ RHMode mode();
+
+ /// Sets the operating mode of the transport.
+ void setMode(RHMode mode);
+
+ /// Sets the transport hardware into low-power sleep mode
+ /// (if supported). May be overridden by specific drivers to initialte sleep mode.
+ /// If successful, the transport will stay in sleep mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// \return true if sleep mode is supported by transport hardware and the RadioHead driver, and if sleep mode
+ /// was successfully entered. If sleep mode is not suported, return false.
+ virtual bool sleep();
+
+ /// Prints a data buffer in HEX.
+ /// For diagnostic use
+ /// \param[in] prompt string to preface the print
+ /// \param[in] buf Location of the buffer to print
+ /// \param[in] len Length of the buffer in octets.
+ static void printBuffer(const char* prompt, const uint8_t* buf, uint8_t len);
+
+ /// Returns the count of the number of bad received packets (ie packets with bad lengths, checksum etc)
+ /// which were rejected and not delivered to the application.
+ /// Caution: not all drivers can correctly report this count. Some underlying hardware only report
+ /// good packets.
+ /// \return The number of bad packets received.
+ uint16_t rxBad();
+
+ /// Returns the count of the number of
+ /// good received packets
+ /// \return The number of good packets received.
+ uint16_t rxGood();
+
+ /// Returns the count of the number of
+ /// packets successfully transmitted (though not necessarily received by the destination)
+ /// \return The number of packets successfully transmitted
+ uint16_t txGood();
+
+protected:
+
+ /// The current transport operating mode
+ volatile RHMode _mode;
+
+ /// This node id
+ uint8_t _thisAddress;
+
+ /// Whether the transport is in promiscuous mode
+ bool _promiscuous;
+
+ /// TO header in the last received mesasge
+ volatile uint8_t _rxHeaderTo;
+
+ /// FROM header in the last received mesasge
+ volatile uint8_t _rxHeaderFrom;
+
+ /// ID header in the last received mesasge
+ volatile uint8_t _rxHeaderId;
+
+ /// FLAGS header in the last received mesasge
+ volatile uint8_t _rxHeaderFlags;
+
+ /// TO header to send in all messages
+ uint8_t _txHeaderTo;
+
+ /// FROM header to send in all messages
+ uint8_t _txHeaderFrom;
+
+ /// ID header to send in all messages
+ uint8_t _txHeaderId;
+
+ /// FLAGS header to send in all messages
+ uint8_t _txHeaderFlags;
+
+ /// The value of the last received RSSI value, in some transport specific units
+ volatile int8_t _lastRssi;
+
+ /// Count of the number of bad messages (eg bad checksum etc) received
+ volatile uint16_t _rxBad;
+
+ /// Count of the number of successfully transmitted messaged
+ volatile uint16_t _rxGood;
+
+ /// Count of the number of bad messages (correct checksum etc) received
+ volatile uint16_t _txGood;
+
+private:
+
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericSPI.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,31 @@
+// RHGenericSPI.cpp
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// Contributed by Joanna Rutkowska
+// $Id: RHGenericSPI.cpp,v 1.2 2014/04/12 05:26:05 mikem Exp $
+
+#include <RHGenericSPI.h>
+
+RHGenericSPI::RHGenericSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode)
+ :
+ _frequency(frequency),
+ _bitOrder(bitOrder),
+ _dataMode(dataMode)
+{
+}
+
+void RHGenericSPI::setBitOrder(BitOrder bitOrder)
+{
+ _bitOrder = bitOrder;
+}
+
+void RHGenericSPI::setDataMode(DataMode dataMode)
+{
+ _dataMode = dataMode;
+}
+
+void RHGenericSPI::setFrequency(Frequency frequency)
+{
+ _frequency = frequency;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericSPI.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,137 @@
+// RHGenericSPI.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// Contributed by Joanna Rutkowska
+// $Id: RHGenericSPI.h,v 1.7 2014/04/14 08:37:11 mikem Exp $
+
+#ifndef RHGenericSPI_h
+#define RHGenericSPI_h
+
+#include <RadioHead.h>
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHGenericSPI RHGenericSPI.h <RHGenericSPI.h>
+/// \brief Base class for SPI interfaces
+///
+/// This generic abstract class is used to encapsulate hardware or software SPI interfaces for
+/// a variety of platforms.
+/// The intention is so that driver classes can be configured to use hardware or software SPI
+/// without changing the main code.
+///
+/// You must provide a subclass of this class to driver constructors that require SPI.
+/// A concrete subclass that encapsualates the standard Arduino hardware SPI and a bit-banged
+/// software implementation is included.
+///
+/// Do not directly use this class: it must be subclassed and the following abstract functions at least
+/// must be implmented:
+/// - begin()
+/// - end()
+/// - transfer()
+class RHGenericSPI
+{
+public:
+
+ /// \brief Defines constants for different SPI modes
+ ///
+ /// Defines constants for different SPI modes
+ /// that can be passed to the constructor or setMode()
+ /// We need to define these in a device and platform independent way, because the
+ /// SPI implementation is different on each platform.
+ typedef enum
+ {
+ DataMode0 = 0, ///< SPI Mode 0: CPOL = 0, CPHA = 0
+ DataMode1, ///< SPI Mode 1: CPOL = 0, CPHA = 1
+ DataMode2, ///< SPI Mode 2: CPOL = 1, CPHA = 0
+ DataMode3, ///< SPI Mode 3: CPOL = 1, CPHA = 1
+ } DataMode;
+
+ /// \brief Defines constants for different SPI bus frequencies
+ ///
+ /// Defines constants for different SPI bus frequencies
+ /// that can be passed to setFrequency().
+ /// The frequency you get may not be exactly the one according to the name.
+ /// We need to define these in a device and platform independent way, because the
+ /// SPI implementation is different on each platform.
+ typedef enum
+ {
+ Frequency1MHz = 0, ///< SPI bus frequency close to 1MHz
+ Frequency2MHz, ///< SPI bus frequency close to 2MHz
+ Frequency4MHz, ///< SPI bus frequency close to 4MHz
+ Frequency8MHz, ///< SPI bus frequency close to 8MHz
+ Frequency16MHz ///< SPI bus frequency close to 16MHz
+ } Frequency;
+
+ /// \brief Defines constants for different SPI endianness
+ ///
+ /// Defines constants for different SPI endianness
+ /// that can be passed to setBitOrder()
+ /// We need to define these in a device and platform independent way, because the
+ /// SPI implementation is different on each platform.
+ typedef enum
+ {
+ BitOrderMSBFirst = 0, ///< SPI MSB first
+ BitOrderLSBFirst, ///< SPI LSB first
+ } BitOrder;
+
+ /// Constructor
+ /// Creates an instance of an abstract SPI interface.
+ /// Do not use this contructor directly: you must instead use on of the concrete subclasses provided
+ /// such as RHHardwareSPI or RHSoftwareSPI
+ /// \param[in] frequency One of RHGenericSPI::Frequency to select the SPI bus frequency. The frequency
+ /// is mapped to the closest available bus frequency on the platform.
+ /// \param[in] bitOrder Select the SPI bus bit order, one of RHGenericSPI::BitOrderMSBFirst or
+ /// RHGenericSPI::BitOrderLSBFirst.
+ /// \param[in] dataMode Selects the SPI bus data mode. One of RHGenericSPI::DataMode
+ RHGenericSPI(Frequency frequency = Frequency1MHz, BitOrder bitOrder = BitOrderMSBFirst, DataMode dataMode = DataMode0);
+
+ /// Transfer a single octet to and from the SPI interface
+ /// \param[in] data The octet to send
+ /// \return The octet read from SPI while the data octet was sent
+ virtual uint8_t transfer(uint8_t data) = 0;
+
+ /// SPI Configuration methods
+ /// Enable SPI interrupts (if supported)
+ /// This can be used in an SPI slave to indicate when an SPI message has been received
+ virtual void attachInterrupt() {};
+
+ /// Disable SPI interrupts (if supported)
+ /// This can be used to diable the SPI interrupt in slaves where that is supported.
+ virtual void detachInterrupt() {};
+
+ /// Initialise the SPI library.
+ /// Call this after configuring and before using the SPI library
+ virtual void begin() = 0;
+
+ /// Disables the SPI bus (leaving pin modes unchanged).
+ /// Call this after you have finished using the SPI interface
+ virtual void end() = 0;
+
+ /// Sets the bit order the SPI interface will use
+ /// Sets the order of the bits shifted out of and into the SPI bus, either
+ /// LSBFIRST (least-significant bit first) or MSBFIRST (most-significant bit first).
+ /// \param[in] bitOrder Bit order to be used: one of RHGenericSPI::BitOrder
+ virtual void setBitOrder(BitOrder bitOrder);
+
+ /// Sets the SPI data mode: that is, clock polarity and phase.
+ /// See the Wikipedia article on SPI for details.
+ /// \param[in] dataMode The mode to use: one of RHGenericSPI::DataMode
+ virtual void setDataMode(DataMode dataMode);
+
+ /// Sets the SPI clock divider relative to the system clock.
+ /// On AVR based boards, the dividers available are 2, 4, 8, 16, 32, 64 or 128.
+ /// The default setting is SPI_CLOCK_DIV4, which sets the SPI clock to one-quarter
+ /// the frequency of the system clock (4 Mhz for the boards at 16 MHz).
+ /// \param[in] frequency The data rate to use: one of RHGenericSPI::Frequency
+ virtual void setFrequency(Frequency frequency);
+
+protected:
+ /// The configure SPI Bus frequency, one of RHGenericSPI::Frequency
+ Frequency _frequency; // Bus frequency, one of RHGenericSPI::Frequency
+
+ /// Bit order, one of RHGenericSPI::BitOrder
+ BitOrder _bitOrder;
+
+ /// SPI bus mode, one of RHGenericSPI::DataMode
+ DataMode _dataMode;
+};
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHHardwareSPI.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,336 @@
+// RHHardwareSPI.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// Contributed by Joanna Rutkowska
+// $Id: RHHardwareSPI.cpp,v 1.12 2015/07/01 00:46:05 mikem Exp $
+
+#include <RHHardwareSPI.h>
+
+// Declare a single default instance of the hardware SPI interface class
+RHHardwareSPI hardware_spi;
+
+#ifdef RH_HAVE_HARDWARE_SPI
+
+#if (RH_PLATFORM == RH_PLATFORM_STM32) // Maple etc
+// Declare an SPI interface to use
+HardwareSPI _SPI(1);
+#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32F4 Discovery
+// Declare an SPI interface to use
+HardwareSPI _SPI(1);
+#elif (RH_PLATFORM == RH_PLATFORM_MBED) // MBed
+// Declare an SPI interface to use
+#define SPI_MOSI PB_15
+#define SPI_MISO PB_14
+#define SPI_CLK PB_13
+
+SPI _SPI(SPI_MOSI, SPI_MISO, SPI_CLK);
+
+#define REVERSE_BITS(byte) (((reverse_lookup[(byte & 0x0F)]) << 4) + reverse_lookup[((byte & 0xF0) >> 4)])
+
+static const uint8_t reverse_lookup[] = { 0, 8, 4, 12, 2, 10, 6, 14,1, 9, 5, 13,3, 11, 7, 15 };
+
+#endif
+
+// Arduino Due has default SPI pins on central SPI headers, and not on 10, 11, 12, 13
+// as per otherArduinos
+// http://21stdigitalhome.blogspot.com.au/2013/02/arduino-due-hardware-SPI.html
+#if defined (__arm__) && !defined(CORE_TEENSY)
+ // Arduino Due in 1.5.5 has no definitions for SPI dividers
+ // SPI clock divider is based on MCK of 84MHz
+ #define SPI_CLOCK_DIV16 (VARIANT_MCK/84000000) // 1MHz
+ #define SPI_CLOCK_DIV8 (VARIANT_MCK/42000000) // 2MHz
+ #define SPI_CLOCK_DIV4 (VARIANT_MCK/21000000) // 4MHz
+ #define SPI_CLOCK_DIV2 (VARIANT_MCK/10500000) // 8MHz
+ #define SPI_CLOCK_DIV1 (VARIANT_MCK/5250000) // 16MHz
+#endif
+
+RHHardwareSPI::RHHardwareSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode)
+ :
+ RHGenericSPI(frequency, bitOrder, dataMode)
+{
+}
+
+uint8_t RHHardwareSPI::transfer(uint8_t data)
+{
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ if (_bitOrder == BitOrderLSBFirst)
+ data = REVERSE_BITS(data);
+
+ return _SPI.write(data);
+#else
+ return _SPI.transfer(data);
+#endif
+}
+
+void RHHardwareSPI::attachInterrupt()
+{
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
+ _SPI.attachInterrupt();
+#endif
+}
+
+void RHHardwareSPI::detachInterrupt()
+{
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
+ _SPI.detachInterrupt();
+#endif
+}
+
+void RHHardwareSPI::begin()
+{
+ // Sigh: there are no common symbols for some of these SPI options across all platforms
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_UNO32)
+ uint8_t dataMode;
+ if (_dataMode == DataMode0)
+ dataMode = SPI_MODE0;
+ else if (_dataMode == DataMode1)
+ dataMode = SPI_MODE1;
+ else if (_dataMode == DataMode2)
+ dataMode = SPI_MODE2;
+ else if (_dataMode == DataMode3)
+ dataMode = SPI_MODE3;
+ else
+ dataMode = SPI_MODE0;
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(CORE_TEENSY)
+ // Temporary work-around due to problem where avr_emulation.h does not work properly for the setDataMode() cal
+ SPCR &= ~SPI_MODE_MASK;
+#else
+ _SPI.setDataMode(dataMode);
+#endif
+
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && !defined(CORE_TEENSY)
+ // Arduino Due in 1.5.5 has its own BitOrder :-(
+ ::BitOrder bitOrder;
+#else
+ uint8_t bitOrder;
+#endif
+ if (_bitOrder == BitOrderLSBFirst)
+ bitOrder = LSBFIRST;
+ else
+ bitOrder = MSBFIRST;
+ _SPI.setBitOrder(bitOrder);
+
+ uint8_t divider;
+ switch (_frequency)
+ {
+ case Frequency1MHz:
+ default:
+#if F_CPU == 8000000
+ divider = SPI_CLOCK_DIV8;
+#else
+ divider = SPI_CLOCK_DIV16;
+#endif
+ break;
+
+ case Frequency2MHz:
+#if F_CPU == 8000000
+ divider = SPI_CLOCK_DIV4;
+#else
+ divider = SPI_CLOCK_DIV8;
+#endif
+ break;
+
+ case Frequency4MHz:
+#if F_CPU == 8000000
+ divider = SPI_CLOCK_DIV2;
+#else
+ divider = SPI_CLOCK_DIV4;
+#endif
+ break;
+
+ case Frequency8MHz:
+ divider = SPI_CLOCK_DIV2; // 4MHz on an 8MHz Arduino
+ break;
+
+ case Frequency16MHz:
+ divider = SPI_CLOCK_DIV2; // Not really 16MHz, only 8MHz. 4MHz on an 8MHz Arduino
+ break;
+
+ }
+ _SPI.setClockDivider(divider);
+ _SPI.begin();
+
+#elif (RH_PLATFORM == RH_PLATFORM_STM32) // Maple etc
+ spi_mode dataMode;
+ // Hmmm, if we do this as a switch, GCC on maple gets v confused!
+ if (_dataMode == DataMode0)
+ dataMode = SPI_MODE_0;
+ else if (_dataMode == DataMode1)
+ dataMode = SPI_MODE_1;
+ else if (_dataMode == DataMode2)
+ dataMode = SPI_MODE_2;
+ else if (_dataMode == DataMode3)
+ dataMode = SPI_MODE_3;
+ else
+ dataMode = SPI_MODE_0;
+
+ uint32 bitOrder;
+ if (_bitOrder == BitOrderLSBFirst)
+ bitOrder = LSBFIRST;
+ else
+ bitOrder = MSBFIRST;
+
+ SPIFrequency frequency; // Yes, I know these are not exact equivalents.
+ switch (_frequency)
+ {
+ case Frequency1MHz:
+ default:
+ frequency = SPI_1_125MHZ;
+ break;
+
+ case Frequency2MHz:
+ frequency = SPI_2_25MHZ;
+ break;
+
+ case Frequency4MHz:
+ frequency = SPI_4_5MHZ;
+ break;
+
+ case Frequency8MHz:
+ frequency = SPI_9MHZ;
+ break;
+
+ case Frequency16MHz:
+ frequency = SPI_18MHZ;
+ break;
+
+ }
+ _SPI.begin(frequency, bitOrder, dataMode);
+
+#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32F4 discovery
+ uint8_t dataMode;
+ if (_dataMode == DataMode0)
+ dataMode = SPI_MODE0;
+ else if (_dataMode == DataMode1)
+ dataMode = SPI_MODE1;
+ else if (_dataMode == DataMode2)
+ dataMode = SPI_MODE2;
+ else if (_dataMode == DataMode3)
+ dataMode = SPI_MODE3;
+ else
+ dataMode = SPI_MODE0;
+
+ uint32_t bitOrder;
+ if (_bitOrder == BitOrderLSBFirst)
+ bitOrder = LSBFIRST;
+ else
+ bitOrder = MSBFIRST;
+
+ SPIFrequency frequency; // Yes, I know these are not exact equivalents.
+ switch (_frequency)
+ {
+ case Frequency1MHz:
+ default:
+ frequency = SPI_1_3125MHZ;
+ break;
+
+ case Frequency2MHz:
+ frequency = SPI_2_625MHZ;
+ break;
+
+ case Frequency4MHz:
+ frequency = SPI_5_25MHZ;
+ break;
+
+ case Frequency8MHz:
+ frequency = SPI_10_5MHZ;
+ break;
+
+ case Frequency16MHz:
+ frequency = SPI_21_0MHZ;
+ break;
+
+ }
+ _SPI.begin(frequency, bitOrder, dataMode);
+#elif (RH_PLATFORM == RH_PLATFORM_RASPI) // Raspberry PI
+ uint8_t dataMode;
+ if (_dataMode == DataMode0)
+ dataMode = BCM2835_SPI_MODE0;
+ else if (_dataMode == DataMode1)
+ dataMode = BCM2835_SPI_MODE1;
+ else if (_dataMode == DataMode2)
+ dataMode = BCM2835_SPI_MODE2;
+ else if (_dataMode == DataMode3)
+ dataMode = BCM2835_SPI_MODE3;
+
+ uint8_t bitOrder;
+ if (_bitOrder == BitOrderLSBFirst)
+ bitOrder = BCM2835_SPI_BIT_ORDER_LSBFIRST;
+ else
+ bitOrder = BCM2835_SPI_BIT_ORDER_MSBFIRST;
+
+ uint32_t divider;
+ switch (_frequency)
+ {
+ case Frequency1MHz:
+ default:
+ divider = BCM2835_SPI_CLOCK_DIVIDER_256;
+ break;
+ case Frequency2MHz:
+ divider = BCM2835_SPI_CLOCK_DIVIDER_128;
+ break;
+ case Frequency4MHz:
+ divider = BCM2835_SPI_CLOCK_DIVIDER_64;
+ break;
+ case Frequency8MHz:
+ divider = BCM2835_SPI_CLOCK_DIVIDER_32;
+ break;
+ case Frequency16MHz:
+ divider = BCM2835_SPI_CLOCK_DIVIDER_16;
+ break;
+ }
+ _SPI.begin(divider, bitOrder, dataMode);
+#elif (RH_PLATFORM == RH_PLATFORM_MBED) // MBed
+ uint8_t dataMode;
+ if (_dataMode == DataMode0)
+ dataMode = 0;
+ else if (_dataMode == DataMode1)
+ dataMode = 1;
+ else if (_dataMode == DataMode2)
+ dataMode = 2;
+ else if (_dataMode == DataMode3)
+ dataMode = 3;
+
+ _SPI.format(8, dataMode);
+
+ int frequency;
+ switch (_frequency)
+ {
+ case Frequency1MHz:
+ default:
+ frequency = 1000000;
+ break;
+
+ case Frequency2MHz:
+ frequency = 2000000;
+ break;
+
+ case Frequency4MHz:
+ frequency = 4000000;
+ break;
+
+ case Frequency8MHz:
+ frequency = 8000000;
+ break;
+
+ case Frequency16MHz:
+ frequency = 16000000;
+ break;
+ }
+ _SPI.frequency(frequency);
+#else
+ #warning RHHardwareSPI does not support this platform yet. Consider adding it and contributing a patch.
+#endif
+}
+
+void RHHardwareSPI::end()
+{
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ // no end for SPI
+#else
+ return _SPI.end();
+#endif
+}
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHHardwareSPI.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,67 @@
+// RHHardwareSPI.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// Contributed by Joanna Rutkowska
+// $Id: RHHardwareSPI.h,v 1.9 2014/08/12 00:54:52 mikem Exp $
+
+#ifndef RHHardwareSPI_h
+#define RHHardwareSPI_h
+
+#include <RHGenericSPI.h>
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHHardwareSPI RHHardwareSPI.h <RHHardwareSPI.h>
+/// \brief Encapsulate a hardware SPI bus interface
+///
+/// This concrete subclass of GenericSPIClass encapsulates the standard Arduino hardware and other
+/// hardware SPI interfaces.
+class RHHardwareSPI : public RHGenericSPI
+{
+#ifdef RH_HAVE_HARDWARE_SPI
+public:
+ /// Constructor
+ /// Creates an instance of a hardware SPI interface, using whatever SPI hardware is available on
+ /// your processor platform. On Arduino and Uno32, uses SPI. On Maple, uses HardwareSPI.
+ /// \param[in] frequency One of RHGenericSPI::Frequency to select the SPI bus frequency. The frequency
+ /// is mapped to the closest available bus frequency on the platform.
+ /// \param[in] bitOrder Select the SPI bus bit order, one of RHGenericSPI::BitOrderMSBFirst or
+ /// RHGenericSPI::BitOrderLSBFirst.
+ /// \param[in] dataMode Selects the SPI bus data mode. One of RHGenericSPI::DataMode
+ RHHardwareSPI(Frequency frequency = Frequency1MHz, BitOrder bitOrder = BitOrderMSBFirst, DataMode dataMode = DataMode0);
+
+ /// Transfer a single octet to and from the SPI interface
+ /// \param[in] data The octet to send
+ /// \return The octet read from SPI while the data octet was sent
+ uint8_t transfer(uint8_t data);
+
+ // SPI Configuration methods
+ /// Enable SPI interrupts
+ /// This can be used in an SPI slave to indicate when an SPI message has been received
+ /// It will cause the SPI_STC_vect interrupt vectr to be executed
+ void attachInterrupt();
+
+ /// Disable SPI interrupts
+ /// This can be used to diable the SPI interrupt in slaves where that is supported.
+ void detachInterrupt();
+
+ /// Initialise the SPI library
+ /// Call this after configuring the SPI interface and before using it to transfer data.
+ /// Initializes the SPI bus by setting SCK, MOSI, and SS to outputs, pulling SCK and MOSI low, and SS high.
+ void begin();
+
+ /// Disables the SPI bus (leaving pin modes unchanged).
+ /// Call this after you have finished using the SPI interface.
+ void end();
+#else
+ // not supported on ATTiny etc
+ uint8_t transfer(uint8_t data) {return 0;}
+ void begin(){}
+ void end(){}
+
+#endif
+};
+
+// Built in default instance
+extern RHHardwareSPI hardware_spi;
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHMesh.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,244 @@
+// RHMesh.cpp
+//
+// Define addressed datagram
+//
+// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers
+// (see http://www.hoperf.com)
+// RHDatagram will be received only by the addressed node or all nodes within range if the
+// to address is RH_BROADCAST_ADDRESS
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHMesh.cpp,v 1.9 2015/08/13 02:45:47 mikem Exp $
+
+#include <RHMesh.h>
+
+uint8_t RHMesh::_tmpMessage[RH_ROUTER_MAX_MESSAGE_LEN];
+
+////////////////////////////////////////////////////////////////////
+// Constructors
+RHMesh::RHMesh(RHGenericDriver& driver, uint8_t thisAddress)
+ : RHRouter(driver, thisAddress)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+
+////////////////////////////////////////////////////////////////////
+// Discovers a route to the destination (if necessary), sends and
+// waits for delivery to the next hop (but not for delivery to the final destination)
+uint8_t RHMesh::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address, uint8_t flags)
+{
+ if (len > RH_MESH_MAX_MESSAGE_LEN)
+ return RH_ROUTER_ERROR_INVALID_LENGTH;
+
+ if (address != RH_BROADCAST_ADDRESS)
+ {
+ RoutingTableEntry* route = getRouteTo(address);
+ if (!route && !doArp(address))
+ return RH_ROUTER_ERROR_NO_ROUTE;
+ }
+
+ // Now have a route. Contruct an application layer message and send it via that route
+ MeshApplicationMessage* a = (MeshApplicationMessage*)&_tmpMessage;
+ a->header.msgType = RH_MESH_MESSAGE_TYPE_APPLICATION;
+ memcpy(a->data, buf, len);
+ return RHRouter::sendtoWait(_tmpMessage, sizeof(RHMesh::MeshMessageHeader) + len, address, flags);
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHMesh::doArp(uint8_t address)
+{
+ // Need to discover a route
+ // Broadcast a route discovery message with nothing in it
+ MeshRouteDiscoveryMessage* p = (MeshRouteDiscoveryMessage*)&_tmpMessage;
+ p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST;
+ p->destlen = 1;
+ p->dest = address; // Who we are looking for
+ uint8_t error = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 2, RH_BROADCAST_ADDRESS);
+ if (error != RH_ROUTER_ERROR_NONE)
+ return false;
+
+ // Wait for a reply, which will be unicast back to us
+ // It will contain the complete route to the destination
+ uint8_t messageLen = sizeof(_tmpMessage);
+ // FIXME: timeout should be configurable
+ unsigned long starttime = millis();
+ int32_t timeLeft;
+ while ((timeLeft = RH_MESH_ARP_TIMEOUT - (millis() - starttime)) > 0)
+ {
+ if (waitAvailableTimeout(timeLeft))
+ {
+ if (RHRouter::recvfromAck(_tmpMessage, &messageLen))
+ {
+ if ( messageLen > 1
+ && p->header.msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE)
+ {
+ // Got a reply, now add the next hop to the dest to the routing table
+ // The first hop taken is the first octet
+ addRouteTo(address, headerFrom());
+ return true;
+ }
+ }
+ }
+ YIELD;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////
+// Called by RHRouter::recvfromAck whenever a message goes past
+void RHMesh::peekAtMessage(RoutedMessage* message, uint8_t messageLen)
+{
+ MeshMessageHeader* m = (MeshMessageHeader*)message->data;
+ if ( messageLen > 1
+ && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE)
+ {
+ // This is a unicast RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE messages
+ // being routed back to the originator here. Want to scrape some routing data out of the response
+ // We can find the routes to all the nodes between here and the responding node
+ MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)message->data;
+ addRouteTo(d->dest, headerFrom());
+ uint8_t numRoutes = messageLen - sizeof(RoutedMessageHeader) - sizeof(MeshMessageHeader) - 2;
+ uint8_t i;
+ // Find us in the list of nodes that were traversed to get to the responding node
+ for (i = 0; i < numRoutes; i++)
+ if (d->route[i] == _thisAddress)
+ break;
+ i++;
+ while (i++ < numRoutes)
+ addRouteTo(d->route[i], headerFrom());
+ }
+ else if ( messageLen > 1
+ && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE)
+ {
+ MeshRouteFailureMessage* d = (MeshRouteFailureMessage*)message->data;
+ deleteRouteTo(d->dest);
+ }
+}
+
+////////////////////////////////////////////////////////////////////
+// This is called when a message is to be delivered to the next hop
+uint8_t RHMesh::route(RoutedMessage* message, uint8_t messageLen)
+{
+ uint8_t from = headerFrom(); // Might get clobbered during call to superclass route()
+ uint8_t ret = RHRouter::route(message, messageLen);
+ if ( ret == RH_ROUTER_ERROR_NO_ROUTE
+ || ret == RH_ROUTER_ERROR_UNABLE_TO_DELIVER)
+ {
+ // Cant deliver to the next hop. Delete the route
+ deleteRouteTo(message->header.dest);
+ if (message->header.source != _thisAddress)
+ {
+ // This is being proxied, so tell the originator about it
+ MeshRouteFailureMessage* p = (MeshRouteFailureMessage*)&_tmpMessage;
+ p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE;
+ p->dest = message->header.dest; // Who you were trying to deliver to
+ // Make sure there is a route back towards whoever sent the original message
+ addRouteTo(message->header.source, from);
+ ret = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 1, message->header.source);
+ }
+ }
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////
+// Subclasses may want to override
+bool RHMesh::isPhysicalAddress(uint8_t* address, uint8_t addresslen)
+{
+ // Can only handle physical addresses 1 octet long, which is the physical node address
+ return addresslen == 1 && address[0] == _thisAddress;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHMesh::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
+{
+ uint8_t tmpMessageLen = sizeof(_tmpMessage);
+ uint8_t _source;
+ uint8_t _dest;
+ uint8_t _id;
+ uint8_t _flags;
+ if (RHRouter::recvfromAck(_tmpMessage, &tmpMessageLen, &_source, &_dest, &_id, &_flags))
+ {
+ MeshMessageHeader* p = (MeshMessageHeader*)&_tmpMessage;
+
+ if ( tmpMessageLen >= 1
+ && p->msgType == RH_MESH_MESSAGE_TYPE_APPLICATION)
+ {
+ MeshApplicationMessage* a = (MeshApplicationMessage*)p;
+ // Handle application layer messages, presumably for our caller
+ if (source) *source = _source;
+ if (dest) *dest = _dest;
+ if (id) *id = _id;
+ if (flags) *flags = _flags;
+ uint8_t msgLen = tmpMessageLen - sizeof(MeshMessageHeader);
+ if (*len > msgLen)
+ *len = msgLen;
+ memcpy(buf, a->data, *len);
+
+ return true;
+ }
+ else if ( _dest == RH_BROADCAST_ADDRESS
+ && tmpMessageLen > 1
+ && p->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST)
+ {
+ MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)p;
+ // Handle Route discovery requests
+ // Message is an array of node addresses the route request has already passed through
+ // If it originally came from us, ignore it
+ if (_source == _thisAddress)
+ return false;
+
+ uint8_t numRoutes = tmpMessageLen - sizeof(MeshMessageHeader) - 2;
+ uint8_t i;
+ // Are we already mentioned?
+ for (i = 0; i < numRoutes; i++)
+ if (d->route[i] == _thisAddress)
+ return false; // Already been through us. Discard
+
+ // Hasnt been past us yet, record routes back to the earlier nodes
+ addRouteTo(_source, headerFrom()); // The originator
+ for (i = 0; i < numRoutes; i++)
+ addRouteTo(d->route[i], headerFrom());
+ if (isPhysicalAddress(&d->dest, d->destlen))
+ {
+ // This route discovery is for us. Unicast the whole route back to the originator
+ // as a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE
+ // We are certain to have a route there, because we just got it
+ d->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE;
+ RHRouter::sendtoWait((uint8_t*)d, tmpMessageLen, _source);
+ }
+ else if (i < _max_hops)
+ {
+ // Its for someone else, rebroadcast it, after adding ourselves to the list
+ d->route[numRoutes] = _thisAddress;
+ tmpMessageLen++;
+ // Have to impersonate the source
+ // REVISIT: if this fails what can we do?
+ RHRouter::sendtoFromSourceWait(_tmpMessage, tmpMessageLen, RH_BROADCAST_ADDRESS, _source);
+ }
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHMesh::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{
+ unsigned long starttime = millis();
+ int32_t timeLeft;
+ while ((timeLeft = timeout - (millis() - starttime)) > 0)
+ {
+ if (waitAvailableTimeout(timeLeft))
+ {
+ if (recvfromAck(buf, len, from, to, id, flags))
+ return true;
+ YIELD;
+ }
+ }
+ return false;
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHMesh.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,262 @@
+// RHMesh.h
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHMesh.h,v 1.15 2015/08/13 02:45:47 mikem Exp $
+
+#ifndef RHMesh_h
+#define RHMesh_h
+
+#include <RHRouter.h>
+
+// Types of RHMesh message, used to set msgType in the RHMeshHeader
+#define RH_MESH_MESSAGE_TYPE_APPLICATION 0
+#define RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST 1
+#define RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE 2
+#define RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE 3
+
+// Timeout for address resolution in milliecs
+#define RH_MESH_ARP_TIMEOUT 4000
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHMesh RHMesh.h <RHMesh.h>
+/// \brief RHRouter subclass for sending addressed, optionally acknowledged datagrams
+/// multi-hop routed across a network, with automatic route discovery
+///
+/// Manager class that extends RHRouter to add automatic route discovery within a mesh of adjacent nodes,
+/// and route signalling.
+///
+/// Unlike RHRouter, RHMesh can be used in networks where the network topology is fluid, or unknown,
+/// or if nodes can mode around or go in or out of service. When a node wants to send a
+/// message to another node, it will automatically discover a route to the destination node and use it.
+/// If the route becomes unavailable, a new route will be discovered.
+///
+/// \par Route Discovery
+///
+/// When a RHMesh mesh node is initialised, it doe not know any routes to any other nodes
+/// (see RHRouter for details on route and the routing table).
+/// When you attempt to send a message with sendtoWait, will first check to see if there is a route to the
+/// destinastion node in the routing tabl;e. If not, it wil initialite 'Route Discovery'.
+/// When a node needs to discover a route to another node, it broadcasts MeshRouteDiscoveryMessage
+/// with a message type of RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST.
+/// Any node that receives such a request checks to see if it is a request for a route to itself
+/// (in which case it makes a unicast reply to the originating node with a
+/// MeshRouteDiscoveryMessage
+/// with a message type of RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE)
+/// otherwise it rebroadcasts the request, after adding itself to the list of nodes visited so
+/// far by the request.
+///
+/// If a node receives a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST that already has itself
+/// listed in the visited nodes, it knows it has already seen and rebroadcast this request,
+/// and threfore ignores it. This prevents broadcast storms.
+/// When a node receives a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST it can use the list of
+/// nodes aready visited to deduce routes back towards the originating (requesting node).
+/// This also means that when the destination node of the request is reached, it (and all
+/// the previous nodes the request visited) will have a route back to the originating node.
+/// This means the unicast RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE
+/// reply will be routed successfully back to the original route requester.
+///
+/// The RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE sent back by the destination node contains
+/// the full list of nodes that were visited on the way to the destination.
+/// Therefore, intermediate nodes that route the reply back towards the originating node can use the
+/// node list in the reply to deduce routes to all the nodes between it and the destination node.
+///
+/// Therefore, RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST and
+/// RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE together ensure the original requester and all
+/// the intermediate nodes know how to route to the source and destination nodes and every node along the path.
+///
+/// Note that there is a race condition here that can effect routing on multipath routes. For example,
+/// if the route to the destination can traverse several paths, last reply from the destination
+/// will be the one used.
+///
+/// \par Route Failure
+///
+/// RHRouter (and therefore RHMesh) use reliable hop-to-hop delivery of messages using
+/// hop-to-hop acknowledgements, but not end-to-end acknowledgements. When sendtoWait() returns,
+/// you know that the message has been delivered to the next hop, but not if it is (or even if it can be)
+/// delivered to the destination node. If during the course of hop-to-hop routing of a message,
+/// one of the intermediate RHMesh nodes finds it cannot deliver to the next hop
+/// (say due to a lost route or no acknwledgement from the next hop), it replies to the
+/// originator with a unicast MeshRouteFailureMessage RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE message.
+/// Intermediate nodes (on the way beack to the originator)
+/// and the originating node use this message to delete the route to the destination
+/// node of the original message. This means that if a route to a destination becomes unusable
+/// (either because an intermediate node is off the air, or has moved out of range) a new route
+/// will be established the next time a message is to be sent.
+///
+/// \par Message Format
+///
+/// RHMesh uses a number of message formats layered on top of RHRouter:
+/// - MeshApplicationMessage (message type RH_MESH_MESSAGE_TYPE_APPLICATION).
+/// Carries an application layer message for the caller of RHMesh
+/// - MeshRouteDiscoveryMessage (message types RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST
+/// and RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE). Carries Route Discovery messages
+/// (broadcast) and replies (unicast).
+/// - MeshRouteFailureMessage (message type RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE) Informs nodes of
+/// route failures.
+///
+/// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers
+/// (see http://www.hoperf.com)
+///
+/// \par Memory
+///
+/// RHMesh programs require significant amount of SRAM, often approaching 2kbytes,
+/// which is beyond or at the limits of some Arduinos and other processors. Programs
+/// with additional software besides basic RHMesh programs may well require even more. If you have insufficient
+/// SRAM for your program, it may result in failure to run, or wierd crashes and other hard to trace behaviour.
+/// In this event you should consider a processor with more SRAM, such as the MotienoMEGA with 16k
+/// (https://lowpowerlab.com/shop/moteinomega) or others.
+///
+/// \par Performance
+/// This class (in the interests of simple implemtenation and low memory use) does not have
+/// message queueing. This means that only one message at a time can be handled. Message transmission
+/// failures can have a severe impact on network performance.
+/// If you need high performance mesh networking under all conditions consider XBee or similar.
+class RHMesh : public RHRouter
+{
+public:
+
+ /// The maximum length permitted for the application payload data in a RHMesh message
+ #define RH_MESH_MAX_MESSAGE_LEN (RH_ROUTER_MAX_MESSAGE_LEN - sizeof(RHMesh::MeshMessageHeader))
+
+ /// Structure of the basic RHMesh header.
+ typedef struct
+ {
+ uint8_t msgType; ///< Type of RHMesh message, one of RH_MESH_MESSAGE_TYPE_*
+ } MeshMessageHeader;
+
+ /// Signals an application layer message for the caller of RHMesh
+ typedef struct
+ {
+ MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_APPLICATION
+ uint8_t data[RH_MESH_MAX_MESSAGE_LEN]; ///< Application layer payload data
+ } MeshApplicationMessage;
+
+ /// Signals a route discovery request or reply (At present only supports physical dest addresses of length 1 octet)
+ typedef struct
+ {
+ MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_*
+ uint8_t destlen; ///< Reserved. Must be 1.g
+ uint8_t dest; ///< The address of the destination node whose route is being sought
+ uint8_t route[RH_MESH_MAX_MESSAGE_LEN - 1]; ///< List of node addresses visited so far. Length is implcit
+ } MeshRouteDiscoveryMessage;
+
+ /// Signals a route failure
+ typedef struct
+ {
+ MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE
+ uint8_t dest; ///< The address of the destination towards which the route failed
+ } MeshRouteFailureMessage;
+
+ /// Constructor.
+ /// \param[in] driver The RadioHead driver to use to transport messages.
+ /// \param[in] thisAddress The address to assign to this node. Defaults to 0
+ RHMesh(RHGenericDriver& driver, uint8_t thisAddress = 0);
+
+ /// Sends a message to the destination node. Initialises the RHRouter message header
+ /// (the SOURCE address is set to the address of this node, HOPS to 0) and calls
+ /// route() which looks up in the routing table the next hop to deliver to.
+ /// If no route is known, initiates route discovery and waits for a reply.
+ /// Then sends the message to the next hop
+ /// Then waits for an acknowledgement from the next hop
+ /// (but not from the destination node (if that is different).
+ /// \param [in] buf The application message data
+ /// \param [in] len Number of octets in the application message data. 0 is permitted
+ /// \param [in] dest The destination node address. If the address is RH_BROADCAST_ADDRESS (255)
+ /// the message will be broadcast to all the nearby nodes, but not routed or relayed.
+ /// \param [in] flags Optional flags for use by subclasses or application layer,
+ /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck().
+ /// \return The result code:
+ /// - RH_ROUTER_ERROR_NONE Message was routed and delivered to the next hop
+ /// (not necessarily to the final dest address)
+ /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table
+ /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Not able to deliver to the next hop
+ /// (usually because it dod not acknowledge due to being off the air or out of range
+ uint8_t sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags = 0);
+
+ /// Starts the receiver if it is not running already, processes and possibly routes any received messages
+ /// addressed to other nodes
+ /// and delivers any messages addressed to this node.
+ /// If there is a valid application layer message available for this node (or RH_BROADCAST_ADDRESS),
+ /// send an acknowledgement to the last hop
+ /// address (blocking until this is complete), then copy the application message payload data
+ /// to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length..
+ /// If from is not NULL, the originator SOURCE address is placed in *source.
+ /// If to is not NULL, the DEST address is placed in *dest. This might be this nodes address or
+ /// RH_BROADCAST_ADDRESS.
+ /// This is the preferred function for getting messages addressed to this node.
+ /// If the message is not a broadcast, acknowledge to the sender before returning.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address
+ /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was received for this node and copied to buf
+ bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+ /// Starts the receiver if it is not running already.
+ /// Similar to recvfromAck(), this will block until either a valid application layer
+ /// message available for this node
+ /// or the timeout expires.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] timeout Maximum time to wait in milliseconds
+ /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address
+ /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was copied to buf
+ bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+protected:
+
+ /// Internal function that inspects messages being received and adjusts the routing table if necessary.
+ /// Called by recvfromAck() immediately after it gets the message from RHReliableDatagram
+ /// \param [in] message Pointer to the RHRouter message that was received.
+ /// \param [in] messageLen Length of message in octets
+ virtual void peekAtMessage(RoutedMessage* message, uint8_t messageLen);
+
+ /// Internal function that inspects messages being received and adjusts the routing table if necessary.
+ /// This is virtual, which lets subclasses override or intercept the route() function.
+ /// Called by sendtoWait after the message header has been filled in.
+ /// \param [in] message Pointer to the RHRouter message to be sent.
+ /// \param [in] messageLen Length of message in octets
+ virtual uint8_t route(RoutedMessage* message, uint8_t messageLen);
+
+ /// Try to resolve a route for the given address. Blocks while discovering the route
+ /// which may take up to 4000 msec.
+ /// Virtual so subclasses can override.
+ /// \param [in] address The physical address to resolve
+ /// \return true if the address was resolved and added to the local routing table
+ virtual bool doArp(uint8_t address);
+
+ /// Tests if the given address of length addresslen is indentical to the
+ /// physical address of this node.
+ /// RHMesh always implements physical addresses as the 1 octet address of the node
+ /// given by _thisAddress
+ /// Called by recvfromAck() to test whether a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST
+ /// is for this node.
+ /// Subclasses may want to override to implement more complicated or longer physical addresses
+ /// \param [in] address Address of the pyysical addres being tested
+ /// \param [in] addresslen Lengthof the address in bytes
+ /// \return true if the physical address of this node is identical to address
+ virtual bool isPhysicalAddress(uint8_t* address, uint8_t addresslen);
+
+private:
+ /// Temporary message buffer
+ static uint8_t _tmpMessage[RH_ROUTER_MAX_MESSAGE_LEN];
+
+};
+
+/// @example rf22_mesh_client.pde
+/// @example rf22_mesh_server1.pde
+/// @example rf22_mesh_server2.pde
+/// @example rf22_mesh_server3.pde
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHNRFSPIDriver.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,95 @@
+// RHNRFSPIDriver.cpp
+//
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHNRFSPIDriver.cpp,v 1.2 2014/05/03 00:20:36 mikem Exp $
+
+#include <RHNRFSPIDriver.h>
+
+RHNRFSPIDriver::RHNRFSPIDriver(PINS slaveSelectPin, RHGenericSPI& spi)
+ :
+ _spi(spi),
+ _slaveSelectPin(slaveSelectPin)
+{
+}
+
+bool RHNRFSPIDriver::init()
+{
+ // start the SPI library with the default speeds etc:
+ // On Arduino Due this defaults to SPI1 on the central group of 6 SPI pins
+ _spi.begin();
+
+ // Initialise the slave select pin
+ // On Maple, this must be _after_ spi.begin
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_slaveSelectPin, OUTPUT);
+#endif
+ digitalWrite(_slaveSelectPin, HIGH);
+
+ delay(100);
+ return true;
+}
+
+// Low level commands for interfacing with the device
+uint8_t RHNRFSPIDriver::spiCommand(uint8_t command)
+{
+ uint8_t status;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(command);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+uint8_t RHNRFSPIDriver::spiRead(uint8_t reg)
+{
+ uint8_t val;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(reg); // Send the address, discard the status
+ val = _spi.transfer(0); // The written value is ignored, reg value is read
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return val;
+}
+
+uint8_t RHNRFSPIDriver::spiWrite(uint8_t reg, uint8_t val)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg); // Send the address
+ _spi.transfer(val); // New value follows
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+uint8_t RHNRFSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg); // Send the start address
+ while (len--)
+ *dest++ = _spi.transfer(0);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+uint8_t RHNRFSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg); // Send the start address
+ while (len--)
+ _spi.transfer(*src++);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHNRFSPIDriver.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,93 @@
+// RHNRFSPIDriver.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHNRFSPIDriver.h,v 1.2 2014/08/12 00:54:52 mikem Exp $
+
+#ifndef RHNRFSPIDriver_h
+#define RHNRFSPIDriver_h
+
+#include <RHGenericDriver.h>
+#include <RHHardwareSPI.h>
+
+class RHGenericSPI;
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHNRFSPIDriver RHNRFSPIDriver.h <RHNRFSPIDriver.h>
+/// \brief Base class for a RadioHead driver that use the SPI bus
+/// to communicate with its transport hardware.
+///
+/// This class can be subclassed by Drivers that require to use the SPI bus.
+/// It can be configured to use either the RHHardwareSPI class (if there is one available on the platform)
+/// of the bitbanged RHSoftwareSPI class. The dfault behaviour is to use a pre-instantiated built-in RHHardwareSPI
+/// interface.
+///
+/// SPI bus access is protected by ATOMIC_BLOCK_START and ATOMIC_BLOCK_END, which will ensure interrupts
+/// are disabled during access.
+///
+/// The read and write routines use SPI conventions as used by Nordic NRF radios and otehr devices,
+/// but these can be overriden
+/// in subclasses if necessary.
+///
+/// Application developers are not expected to instantiate this class directly:
+/// it is for the use of Driver developers.
+class RHNRFSPIDriver : public RHGenericDriver
+{
+public:
+ /// Constructor
+ /// \param[in] slaveSelectPin The controller pin to use to select the desired SPI device. This pin will be driven LOW
+ /// during SPI communications with the SPI device that uis iused by this Driver.
+ /// \param[in] spi Reference to the SPI interface to use. The default is to use a default built-in Hardware interface.
+ RHNRFSPIDriver(PINS slaveSelectPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialise the Driver transport hardware and software.
+ /// Make sure the Driver is properly configured before calling init().
+ /// \return true if initialisation succeeded.
+ bool init();
+
+ /// Sends a single command to the device
+ /// \param[in] command The command code to send to the device.
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiCommand(uint8_t command);
+
+ /// Reads a single register from the SPI device
+ /// \param[in] reg Register number
+ /// \return The value of the register
+ uint8_t spiRead(uint8_t reg);
+
+ /// Writes a single byte to the SPI device
+ /// \param[in] reg Register number
+ /// \param[in] val The value to write
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiWrite(uint8_t reg, uint8_t val);
+
+ /// Reads a number of consecutive registers from the SPI device using burst read mode
+ /// \param[in] reg Register number of the first register
+ /// \param[in] dest Array to write the register values to. Must be at least len bytes
+ /// \param[in] len Number of bytes to read
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len);
+
+ /// Write a number of consecutive registers using burst write mode
+ /// \param[in] reg Register number of the first register
+ /// \param[in] src Array of new register values to write. Must be at least len bytes
+ /// \param[in] len Number of bytes to write
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len);
+
+protected:
+ /// Reference to the RHGenericSPI instance to use to trasnfer data with teh SPI device
+ RHGenericSPI& _spi;
+
+ /// The pin number of the Slave Selct pin that is used to select the desired device.
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ DigitalOut _slaveSelectPin;
+#else
+ uint8_t _slaveSelectPin;
+#endif
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHReliableDatagram.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,189 @@
+// RHReliableDatagram.cpp
+//
+// Define addressed datagram
+//
+// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers
+// (see http://www.hoperf.com)
+// RHDatagram will be received only by the addressed node or all nodes within range if the
+// to address is RH_BROADCAST_ADDRESS
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHReliableDatagram.cpp,v 1.14 2015/08/13 02:45:47 mikem Exp $
+
+#include <RHReliableDatagram.h>
+
+////////////////////////////////////////////////////////////////////
+// Constructors
+RHReliableDatagram::RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress)
+ : RHDatagram(driver, thisAddress)
+{
+ _retransmissions = 0;
+ _lastSequenceNumber = 0;
+ _timeout = RH_DEFAULT_TIMEOUT;
+ _retries = RH_DEFAULT_RETRIES;
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+void RHReliableDatagram::setTimeout(uint16_t timeout)
+{
+ _timeout = timeout;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHReliableDatagram::setRetries(uint8_t retries)
+{
+ _retries = retries;
+}
+
+////////////////////////////////////////////////////////////////////
+uint8_t RHReliableDatagram::retries()
+{
+ return _retries;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address)
+{
+ // Assemble the message
+ uint8_t thisSequenceNumber = ++_lastSequenceNumber;
+ uint8_t retries = 0;
+ while (retries++ <= _retries)
+ {
+ setHeaderId(thisSequenceNumber);
+ setHeaderFlags(RH_FLAGS_NONE, RH_FLAGS_ACK); // Clear the ACK flag
+ sendto(buf, len, address);
+ waitPacketSent();
+
+ // Never wait for ACKS to broadcasts:
+ if (address == RH_BROADCAST_ADDRESS)
+ return true;
+
+ if (retries > 1)
+ _retransmissions++;
+ unsigned long thisSendTime = millis(); // Timeout does not include original transmit time
+
+ // Compute a new timeout, random between _timeout and _timeout*2
+ // This is to prevent collisions on every retransmit
+ // if 2 nodes try to transmit at the same time
+#if (RH_PLATFORM == RH_PLATFORM_RASPI) // use standard library random(), bugs in random(min, max)
+ uint16_t timeout = _timeout + (_timeout * (random() & 0xFF) / 256);
+#elif (RH_PLATFORM == RH_PLATFORM_MBED)
+ uint16_t timeout = _timeout + (_timeout * (rand() & 0xFF) / 256);
+#else
+ uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256);
+#endif
+ int32_t timeLeft;
+ while ((timeLeft = timeout - (millis() - thisSendTime)) > 0)
+ {
+ if (waitAvailableTimeout(timeLeft))
+ {
+ uint8_t from, to, id, flags;
+ if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message
+ {
+ // Now have a message: is it our ACK?
+ if ( from == address
+ && to == _thisAddress
+ && (flags & RH_FLAGS_ACK)
+ && (id == thisSequenceNumber))
+ {
+ // Its the ACK we are waiting for
+ return true;
+ }
+ else if ( !(flags & RH_FLAGS_ACK)
+ && (id == _seenIds[from]))
+ {
+ // This is a request we have already received. ACK it again
+ acknowledge(id, from);
+ }
+ // Else discard it
+ }
+ }
+ // Not the one we are waiting for, maybe keep waiting until timeout exhausted
+ YIELD;
+ }
+ // Timeout exhausted, maybe retry
+ YIELD;
+ }
+ // Retries exhausted
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHReliableDatagram::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{
+ uint8_t _from;
+ uint8_t _to;
+ uint8_t _id;
+ uint8_t _flags;
+ // Get the message before its clobbered by the ACK (shared rx and tx buffer in some drivers
+ if (available() && recvfrom(buf, len, &_from, &_to, &_id, &_flags))
+ {
+ // Never ACK an ACK
+ if (!(_flags & RH_FLAGS_ACK))
+ {
+ // Its a normal message for this node, not an ACK
+ if (_to != RH_BROADCAST_ADDRESS)
+ {
+ // Its not a broadcast, so ACK it
+ // Acknowledge message with ACK set in flags and ID set to received ID
+ acknowledge(_id, _from);
+ }
+ // If we have not seen this message before, then we are interested in it
+ if (_id != _seenIds[_from])
+ {
+ if (from) *from = _from;
+ if (to) *to = _to;
+ if (id) *id = _id;
+ if (flags) *flags = _flags;
+ _seenIds[_from] = _id;
+ return true;
+ }
+ // Else just re-ack it and wait for a new one
+ }
+ }
+ // No message for us available
+ return false;
+}
+
+bool RHReliableDatagram::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{
+ unsigned long starttime = millis();
+ int32_t timeLeft;
+ while ((timeLeft = timeout - (millis() - starttime)) > 0)
+ {
+ if (waitAvailableTimeout(timeLeft))
+ {
+ if (recvfromAck(buf, len, from, to, id, flags))
+ return true;
+ }
+ YIELD;
+ }
+ return false;
+}
+
+uint32_t RHReliableDatagram::retransmissions()
+{
+ return _retransmissions;
+}
+
+void RHReliableDatagram::resetRetransmissions()
+{
+ _retransmissions = 0;
+}
+
+void RHReliableDatagram::acknowledge(uint8_t id, uint8_t from)
+{
+ setHeaderId(id);
+ setHeaderFlags(RH_FLAGS_ACK);
+ // We would prefer to send a zero length ACK,
+ // but if an RH_RF22 receives a 0 length message with a CRC error, it will never receive
+ // a 0 length message again, until its reset, which makes everything hang :-(
+ // So we send an ACK of 1 octet
+ // REVISIT: should we send the RSSI for the information of the sender?
+ uint8_t ack = '!';
+ sendto(&ack, sizeof(ack), from);
+ waitPacketSent();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHReliableDatagram.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,201 @@
+// RHReliableDatagram.h
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHReliableDatagram.h,v 1.16 2015/08/12 23:18:51 mikem Exp $
+
+#ifndef RHReliableDatagram_h
+#define RHReliableDatagram_h
+
+#include <RHDatagram.h>
+
+// The acknowledgement bit in the FLAGS
+// The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved
+// for application layer use.
+#define RH_FLAGS_ACK 0x80
+
+/// the default retry timeout in milliseconds
+#define RH_DEFAULT_TIMEOUT 200
+
+/// The default number of retries
+#define RH_DEFAULT_RETRIES 3
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHReliableDatagram RHReliableDatagram.h <RHReliableDatagram.h>
+/// \brief RHDatagram subclass for sending addressed, acknowledged, retransmitted datagrams.
+///
+/// Manager class that extends RHDatagram to define addressed, reliable datagrams with acknowledgement and retransmission.
+/// Based on RHDatagram, adds flags and sequence numbers. RHReliableDatagram is reliable in the sense
+/// that messages are acknowledged by the recipient, and unacknowledged messages are retransmitted until acknowledged or the
+/// retries are exhausted.
+/// When addressed messages are sent (by sendtoWait()), it will wait for an ack, and retransmit
+/// after timeout until an ack is received or retries are exhausted.
+/// When addressed messages are collected by the application (by recvfromAck()),
+/// an acknowledgement is automatically sent to the sender.
+///
+/// You can use RHReliableDatagram to send broadcast messages, with a TO address of RH_BROADCAST_ADDRESS,
+/// however broadcasts are not acknowledged or retransmitted and are therefore NOT actually reliable.
+///
+/// The retransmit timeout is randomly varied between timeout and timeout*2 to prevent collisions on all
+/// retries when 2 nodes happen to start sending at the same time .
+///
+/// Each new message sent by sendtoWait() has its ID incremented.
+///
+/// An ack consists of a message with:
+/// - TO set to the from address of the original message
+/// - FROM set to this node address
+/// - ID set to the ID of the original message
+/// - FLAGS with the RH_FLAGS_ACK bit set
+/// - 1 octet of payload containing ASCII '!' (since some drivers cannot handle 0 length payloads)
+///
+/// \par Media Access Strategy
+///
+/// RHReliableDatagram and the underlying drivers always transmit as soon as
+/// sendtoWait() is called. RHReliableDatagram waits for an acknowledgement,
+/// and if one is not received after a timeout period the message is
+/// transmitted again. If no acknowledgement is received after several
+/// retries, the transmissions is deemed to have failed.
+/// No contention for media is detected.
+/// This will be recognised as "pure ALOHA".
+/// The addition of Clear Channel Assessment (CCA) is desirable and planned.
+///
+/// There is no message queuing or threading in RHReliableDatagram.
+/// sendtoWait() waits until an acknowledgement is received, retransmitting
+/// up to (by default) 3 retries time with a default 200ms timeout.
+/// During this transmit-acknowledge phase, any received message (other than the expected
+/// acknowledgement) will be ignored. Your sketch will be unresponsive to new messages
+/// until an acknowledgement is received or the retries are exhausted.
+/// Central server-type sketches should be very cautious about their
+/// retransmit strategy and configuration lest they hang for a long time
+/// trying to reply to clients that are unreachable.
+///
+/// Caution: if you have a radio network with a mixture of slow and fast
+/// processors and ReliableDatagrams, you may be affected by race conditions
+/// where the fast processor acknowledges a message before the sender is ready
+/// to process the acknowledgement. Best practice is to use the same processors (and
+/// radios) throughout your network.
+///
+class RHReliableDatagram : public RHDatagram
+{
+public:
+ /// Constructor.
+ /// \param[in] driver The RadioHead driver to use to transport messages.
+ /// \param[in] thisAddress The address to assign to this node. Defaults to 0
+ RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0);
+
+ /// Sets the minimum retransmit timeout. If sendtoWait is waiting for an ack
+ /// longer than this time (in milliseconds),
+ /// it will retransmit the message. Defaults to 200ms. The timeout is measured from the end of
+ /// transmission of the message. It must be at least longer than the the transmit
+ /// time of the acknowledgement (preamble+6 octets) plus the latency/poll time of the receiver.
+ /// For fast modulation schemes you can considerably shorten this time.
+ /// The actual timeout is randomly varied between timeout and timeout*2.
+ /// \param[in] timeout The new timeout period in milliseconds
+ void setTimeout(uint16_t timeout);
+
+ /// Sets the maximum number of retries. Defaults to 3 at construction time.
+ /// If set to 0, each message will only ever be sent once.
+ /// sendtoWait will give up and return false if there is no ack received after all transmissions time out
+ /// and the retries count is exhausted.
+ /// param[in] retries The maximum number a retries.
+ void setRetries(uint8_t retries);
+
+ /// Returns the currently configured maximum retries count.
+ /// Can be changed with setRetries().
+ /// \return The currently configured maximum number of retries.
+ uint8_t retries();
+
+ /// Send the message (with retries) and waits for an ack. Returns true if an acknowledgement is received.
+ /// Synchronous: any message other than the desired ACK received while waiting is discarded.
+ /// Blocks until an ACK is received or all retries are exhausted (ie up to retries*timeout milliseconds).
+ /// If the destination address is the broadcast address RH_BROADCAST_ADDRESS (255), the message will
+ /// be sent as a broadcast, but receiving nodes do not acknowledge, and sendtoWait() returns true immediately
+ /// without waiting for any acknowledgements.
+ /// \param[in] address The address to send the message to.
+ /// \param[in] buf Pointer to the binary message to send
+ /// \param[in] len Number of octets to send
+ /// \return true if the message was transmitted and an acknowledgement was received.
+ bool sendtoWait(uint8_t* buf, uint8_t len, uint8_t address);
+
+ /// If there is a valid message available for this node, send an acknowledgement to the SRC
+ /// address (blocking until this is complete), then copy the message to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length..
+ /// If from is not NULL, the SRC address is placed in *from.
+ /// If to is not NULL, the DEST address is placed in *to.
+ /// This is the preferred function for getting messages addressed to this node.
+ /// If the message is not a broadcast, acknowledge to the sender before returning.
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address
+ /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was copied to buf
+ bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+ /// Similar to recvfromAck(), this will block until either a valid message available for this node
+ /// or the timeout expires. Starts the receiver automatically.
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] timeout Maximum time to wait in milliseconds
+ /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address
+ /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was copied to buf
+ bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+ /// Returns the number of retransmissions
+ /// we have had to send since starting or since the last call to resetRetransmissions().
+ /// \return The number of retransmissions since initialisation.
+ uint32_t retransmissions();
+
+ /// Resets the count of the number of retransmissions
+ /// to 0.
+ void resetRetransmissions();
+
+protected:
+ /// Send an ACK for the message id to the given from address
+ /// Blocks until the ACK has been sent
+ void acknowledge(uint8_t id, uint8_t from);
+
+ /// Checks whether the message currently in the Rx buffer is a new message, not previously received
+ /// based on the from address and the sequence. If it is new, it is acknowledged and returns true
+ /// \return true if there is a message received and it is a new message
+ bool haveNewMessage();
+
+private:
+ /// Count of retransmissions we have had to send
+ uint32_t _retransmissions;
+
+ /// The last sequence number to be used
+ /// Defaults to 0
+ uint8_t _lastSequenceNumber;
+
+ // Retransmit timeout (milliseconds)
+ /// Defaults to 200
+ uint16_t _timeout;
+
+ // Retries (0 means one try only)
+ /// Defaults to 3
+ uint8_t _retries;
+
+ /// Array of the last seen sequence number indexed by node address that sent it
+ /// It is used for duplicate detection. Duplicated messages are re-acknowledged when received
+ /// (this is generally due to lost ACKs, causing the sender to retransmit, even though we have already
+ /// received that message)
+ uint8_t _seenIds[256];
+};
+
+/// @example rf22_reliable_datagram_client.pde
+/// @example rf22_reliable_datagram_server.pde
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHRouter.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,306 @@
+// RHRouter.cpp
+//
+// Define addressed datagram
+//
+// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers
+// (see http://www.hoperf.com)
+// RHDatagram will be received only by the addressed node or all nodes within range if the
+// to address is RH_BROADCAST_ADDRESS
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHRouter.cpp,v 1.7 2015/08/13 02:45:47 mikem Exp $
+
+#include <RHRouter.h>
+
+RHRouter::RoutedMessage RHRouter::_tmpMessage;
+
+////////////////////////////////////////////////////////////////////
+// Constructors
+RHRouter::RHRouter(RHGenericDriver& driver, uint8_t thisAddress)
+ : RHReliableDatagram(driver, thisAddress)
+{
+ _max_hops = RH_DEFAULT_MAX_HOPS;
+ clearRoutingTable();
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+bool RHRouter::init()
+{
+ bool ret = RHReliableDatagram::init();
+ if (ret)
+ _max_hops = RH_DEFAULT_MAX_HOPS;
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::setMaxHops(uint8_t max_hops)
+{
+ _max_hops = max_hops;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::addRouteTo(uint8_t dest, uint8_t next_hop, uint8_t state)
+{
+ uint8_t i;
+
+ // First look for an existing entry we can update
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ {
+ if (_routes[i].dest == dest)
+ {
+ _routes[i].dest = dest;
+ _routes[i].next_hop = next_hop;
+ _routes[i].state = state;
+ return;
+ }
+ }
+
+ // Look for an invalid entry we can use
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ {
+ if (_routes[i].state == Invalid)
+ {
+ _routes[i].dest = dest;
+ _routes[i].next_hop = next_hop;
+ _routes[i].state = state;
+ return;
+ }
+ }
+
+ // Need to make room for a new one
+ retireOldestRoute();
+ // Should be an invalid slot now
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ {
+ if (_routes[i].state == Invalid)
+ {
+ _routes[i].dest = dest;
+ _routes[i].next_hop = next_hop;
+ _routes[i].state = state;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////
+RHRouter::RoutingTableEntry* RHRouter::getRouteTo(uint8_t dest)
+{
+ uint8_t i;
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ if (_routes[i].dest == dest && _routes[i].state != Invalid)
+ return &_routes[i];
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::deleteRoute(uint8_t index)
+{
+ // Delete a route by copying following routes on top of it
+ memcpy(&_routes[index], &_routes[index+1],
+ sizeof(RoutingTableEntry) * (RH_ROUTING_TABLE_SIZE - index - 1));
+ _routes[RH_ROUTING_TABLE_SIZE - 1].state = Invalid;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::printRoutingTable()
+{
+#ifdef RH_HAVE_SERIAL
+ uint8_t i;
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ {
+ Serial.print(i, DEC);
+ Serial.print(" Dest: ");
+ Serial.print(_routes[i].dest, DEC);
+ Serial.print(" Next Hop: ");
+ Serial.print(_routes[i].next_hop, DEC);
+ Serial.print(" State: ");
+ Serial.println(_routes[i].state, DEC);
+ }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::deleteRouteTo(uint8_t dest)
+{
+ uint8_t i;
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ {
+ if (_routes[i].dest == dest)
+ {
+ deleteRoute(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::retireOldestRoute()
+{
+ // We just obliterate the first in the table and clear the last
+ deleteRoute(0);
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::clearRoutingTable()
+{
+ uint8_t i;
+ for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+ _routes[i].state = Invalid;
+}
+
+
+uint8_t RHRouter::sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags)
+{
+ return sendtoFromSourceWait(buf, len, dest, _thisAddress, flags);
+}
+
+////////////////////////////////////////////////////////////////////
+// Waits for delivery to the next hop (but not for delivery to the final destination)
+uint8_t RHRouter::sendtoFromSourceWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t source, uint8_t flags)
+{
+ if (((uint16_t)len + sizeof(RoutedMessageHeader)) > _driver.maxMessageLength())
+ return RH_ROUTER_ERROR_INVALID_LENGTH;
+
+ // Construct a RH RouterMessage message
+ _tmpMessage.header.source = source;
+ _tmpMessage.header.dest = dest;
+ _tmpMessage.header.hops = 0;
+ _tmpMessage.header.id = _lastE2ESequenceNumber++;
+ _tmpMessage.header.flags = flags;
+ memcpy(_tmpMessage.data, buf, len);
+
+ return route(&_tmpMessage, sizeof(RoutedMessageHeader)+len);
+}
+
+////////////////////////////////////////////////////////////////////
+uint8_t RHRouter::route(RoutedMessage* message, uint8_t messageLen)
+{
+ // Reliably deliver it if possible. See if we have a route:
+ uint8_t next_hop = RH_BROADCAST_ADDRESS;
+ if (message->header.dest != RH_BROADCAST_ADDRESS)
+ {
+ RoutingTableEntry* route = getRouteTo(message->header.dest);
+ if (!route)
+ return RH_ROUTER_ERROR_NO_ROUTE;
+ next_hop = route->next_hop;
+ }
+
+ if (!RHReliableDatagram::sendtoWait((uint8_t*)message, messageLen, next_hop))
+ return RH_ROUTER_ERROR_UNABLE_TO_DELIVER;
+
+ return RH_ROUTER_ERROR_NONE;
+}
+
+////////////////////////////////////////////////////////////////////
+// Subclasses may want to override this to peek at messages going past
+void RHRouter::peekAtMessage(RoutedMessage* message, uint8_t messageLen)
+{
+ // Default does nothing
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
+{
+ uint8_t tmpMessageLen = sizeof(_tmpMessage);
+ uint8_t _from;
+ uint8_t _to;
+ uint8_t _id;
+ uint8_t _flags;
+ if (RHReliableDatagram::recvfromAck((uint8_t*)&_tmpMessage, &tmpMessageLen, &_from, &_to, &_id, &_flags))
+ {
+ // Here we simulate networks with limited visibility between nodes
+ // so we can test routing
+#ifdef RH_TEST_NETWORK
+ if (
+#if RH_TEST_NETWORK==1
+ // This network looks like 1-2-3-4
+ (_thisAddress == 1 && _from == 2)
+ || (_thisAddress == 2 && (_from == 1 || _from == 3))
+ || (_thisAddress == 3 && (_from == 2 || _from == 4))
+ || (_thisAddress == 4 && _from == 3)
+
+#elif RH_TEST_NETWORK==2
+ // This network looks like 1-2-4
+ // | | |
+ // --3--
+ (_thisAddress == 1 && (_from == 2 || _from == 3))
+ || _thisAddress == 2
+ || _thisAddress == 3
+ || (_thisAddress == 4 && (_from == 2 || _from == 3))
+
+#elif RH_TEST_NETWORK==3
+ // This network looks like 1-2-4
+ // | |
+ // --3--
+ (_thisAddress == 1 && (_from == 2 || _from == 3))
+ || (_thisAddress == 2 && (_from == 1 || _from == 4))
+ || (_thisAddress == 3 && (_from == 1 || _from == 4))
+ || (_thisAddress == 4 && (_from == 2 || _from == 3))
+
+#elif RH_TEST_NETWORK==4
+ // This network looks like 1-2-3
+ // |
+ // 4
+ (_thisAddress == 1 && _from == 2)
+ || _thisAddress == 2
+ || (_thisAddress == 3 && _from == 2)
+ || (_thisAddress == 4 && _from == 2)
+
+#endif
+)
+ {
+ // OK
+ }
+ else
+ {
+ return false; // Pretend we got nothing
+ }
+#endif
+
+ peekAtMessage(&_tmpMessage, tmpMessageLen);
+ // See if its for us or has to be routed
+ if (_tmpMessage.header.dest == _thisAddress || _tmpMessage.header.dest == RH_BROADCAST_ADDRESS)
+ {
+ // Deliver it here
+ if (source) *source = _tmpMessage.header.source;
+ if (dest) *dest = _tmpMessage.header.dest;
+ if (id) *id = _tmpMessage.header.id;
+ if (flags) *flags = _tmpMessage.header.flags;
+ uint8_t msgLen = tmpMessageLen - sizeof(RoutedMessageHeader);
+ if (*len > msgLen)
+ *len = msgLen;
+ memcpy(buf, _tmpMessage.data, *len);
+ return true; // Its for you!
+ }
+ else if ( _tmpMessage.header.dest != RH_BROADCAST_ADDRESS
+ && _tmpMessage.header.hops++ < _max_hops)
+ {
+ // Maybe it has to be routed to the next hop
+ // REVISIT: if it fails due to no route or unable to deliver to the next hop,
+ // tell the originator. BUT HOW?
+ route(&_tmpMessage, tmpMessageLen);
+ }
+ // Discard it and maybe wait for another
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
+{
+ unsigned long starttime = millis();
+ int32_t timeLeft;
+ while ((timeLeft = timeout - (millis() - starttime)) > 0)
+ {
+ if (waitAvailableTimeout(timeLeft))
+ {
+ if (recvfromAck(buf, len, source, dest, id, flags))
+ return true;
+ }
+ YIELD;
+ }
+ return false;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHRouter.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,328 @@
+// RHRouter.h
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHRouter.h,v 1.9 2014/08/10 20:55:17 mikem Exp $
+
+#ifndef RHRouter_h
+#define RHRouter_h
+
+#include <RHReliableDatagram.h>
+
+// Default max number of hops we will route
+#define RH_DEFAULT_MAX_HOPS 30
+
+// The default size of the routing table we keep
+#define RH_ROUTING_TABLE_SIZE 10
+
+// Error codes
+#define RH_ROUTER_ERROR_NONE 0
+#define RH_ROUTER_ERROR_INVALID_LENGTH 1
+#define RH_ROUTER_ERROR_NO_ROUTE 2
+#define RH_ROUTER_ERROR_TIMEOUT 3
+#define RH_ROUTER_ERROR_NO_REPLY 4
+#define RH_ROUTER_ERROR_UNABLE_TO_DELIVER 5
+
+// This size of RH_ROUTER_MAX_MESSAGE_LEN is OK for Arduino Mega, but too big for
+// Duemilanova. Size of 50 works with the sample router programs on Duemilanova.
+#define RH_ROUTER_MAX_MESSAGE_LEN (RH_MAX_MESSAGE_LEN - sizeof(RHRouter::RoutedMessageHeader))
+//#define RH_ROUTER_MAX_MESSAGE_LEN 50
+
+// These allow us to define a simulated network topology for testing purposes
+// See RHRouter.cpp for details
+//#define RH_TEST_NETWORK 1
+//#define RH_TEST_NETWORK 2
+//#define RH_TEST_NETWORK 3
+//#define RH_TEST_NETWORK 4
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHRouter RHRouter.h <RHRouter.h>
+/// \brief RHReliableDatagram subclass for sending addressed, optionally acknowledged datagrams
+/// multi-hop routed across a network.
+///
+/// Manager class that extends RHReliableDatagram to define addressed messages
+/// That are reliably transmitted and routed across a network. Each message is transmitted reliably
+/// between each hop in order to get from the source node to the destination node.
+///
+/// With RHRouter, routes are hard wired. This means that each node must have programmed
+/// in it how to reach each of the other nodes it will be trying to communicate with.
+/// This means you must specify the next-hop node address for each of the destination nodes,
+/// using the addRouteTo() function.
+///
+/// When sendtoWait() is called with a new message to deliver, and the destination address,
+/// RHRouter looks up the next hop node for the destination node. It then uses
+/// RHReliableDatagram to (reliably) deliver the message to the next hop
+/// (which is expected also to be running an RHRouter). If that next-hop node is not
+/// the final destination, it will also look up the next hop for the destination node and
+/// (reliably) deliver the message to the next hop. By this method, messages can be delivered
+/// across a network of nodes, even if each node cannot hear all of the others in the network.
+/// Each time a message is received for another node and retransmitted to the next hop,
+/// the HOPS filed in teh header is incremented. If a message is received for routing to another node
+/// which has exceed the routers max_hops, the message wioll be dropped and ignored.
+/// This helps prevent infinite routing loops.
+///
+/// RHRouter supports messages with a dest of RH_BROADCAST_ADDRESS. Such messages are not routed,
+/// and are broadcast (once) to all nodes within range.
+///
+/// The recvfromAck() function is responsible not just for receiving and delivering
+/// messages addressed to this node (or RH_BROADCAST_ADDRESS), but
+/// it is also responsible for routing other message to their next hop. This means that it is important to
+/// call recvfromAck() or recvfromAckTimeout() frequently in your main loop. recvfromAck() will return
+/// false if it receives a message but it is not for this node.
+///
+/// RHRouter does not provide reliable end-to-end delivery, but uses reliable hop-to-hop delivery.
+/// If a message is unable to be delivered to an end node during to a delivery failure between 2 hops,
+/// the source node will not be told about it.
+///
+/// Note: This class is most useful for networks of nodes that are essentially static
+/// (i.e. the nodes dont move around), and for which the
+/// routing never changes. If that is not the case for your proposed network, see RHMesh instead.
+///
+/// \par The Routing Table
+///
+/// The routing table is a local table in RHRouter that holds the information about the next hop node
+/// address for each destination address you may want to send a message to. It is your responsibility
+/// to make sure every node in an RHRouter network has been configured with a unique address and the
+/// routing information so that messages are correctly routed across the network from source node to
+/// destination node. This is usually done once in setup() by calling addRouteTo().
+/// The hardwired routing will in general be different on each node, and will depend on the physical
+/// topololgy of the network.
+/// You can also use addRouteTo() to change a route and
+/// deleteRouteTo() to delete a route at run time. Youcan also clear the entire routing table
+///
+/// The Routing Table has limited capacity for entries (defined by RH_ROUTING_TABLE_SIZE, which is 10)
+/// if more than RH_ROUTING_TABLE_SIZE are added, the oldest (first) one will be removed by calling
+/// retireOldestRoute()
+///
+/// \par Message Format
+///
+/// RHRouter add to the lower level RHReliableDatagram (and even lower level RH) class message formats.
+/// In those lower level classes, the hop-to-hop message headers are in the RH message headers,
+/// and are handled automcatically by tyhe RH hardware.
+/// RHRouter and its subclasses add an end-to-end addressing header in the payload of the RH message,
+/// and before the RHRouter application data.
+/// - 1 octet DEST, the destination node address (ie the address of the final
+/// destination node for this message)
+/// - 1 octet SOURCE, the source node address (ie the address of the originating node that first sent
+/// the message).
+/// - 1 octet HOPS, the number of hops this message has traversed so far.
+/// - 1 octet ID, an incrementing message ID for end-to-end message tracking for use by subclasses.
+/// Not used by RHRouter.
+/// - 1 octet FLAGS, a bitmask for use by subclasses. Not used by RHRouter.
+/// - 0 or more octets DATA, the application payload data. The length of this data is implicit
+/// in the length of the entire message.
+///
+/// You should be careful to note that there are ID and FLAGS fields in the low level per-hop
+/// message header too. These are used only for hop-to-hop, and in general will be different to
+/// the ones at the RHRouter level.
+///
+/// \par Testing
+///
+/// Bench testing of such networks is notoriously difficult, especially simulating limited radio
+/// connectivity between some nodes.
+/// To assist testing (both during RH development and for your own networks)
+/// RHRouter.cpp has the ability to
+/// simulate a number of different small network topologies. Each simulated network supports 4 nodes with
+/// addresses 1 to 4. It operates by pretending to not hear RH messages from certain other nodes.
+/// You can enable testing with a \#define TEST_NETWORK in RHRouter.h
+/// The sample programs rf22_mesh_* rely on this feature.
+///
+/// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers
+/// (see http://www.hoperf.com)
+class RHRouter : public RHReliableDatagram
+{
+public:
+
+ /// Defines the structure of the RHRouter message header, used to keep track of end-to-end delivery parameters
+ typedef struct
+ {
+ uint8_t dest; ///< Destination node address
+ uint8_t source; ///< Originator node address
+ uint8_t hops; ///< Hops traversed so far
+ uint8_t id; ///< Originator sequence number
+ uint8_t flags; ///< Originator flags
+ // Data follows, Length is implicit in the overall message length
+ } RoutedMessageHeader;
+
+ /// Defines the structure of a RHRouter message
+ typedef struct
+ {
+ RoutedMessageHeader header; ///< end-to-end delivery header
+ uint8_t data[RH_ROUTER_MAX_MESSAGE_LEN]; ///< Application payload data
+ } RoutedMessage;
+
+ /// Values for the possible states for routes
+ typedef enum
+ {
+ Invalid = 0, ///< No valid route is known
+ Discovering, ///< Discovering a route (not currently used)
+ Valid ///< Route is valid
+ } RouteState;
+
+ /// Defines an entry in the routing table
+ typedef struct
+ {
+ uint8_t dest; ///< Destination node address
+ uint8_t next_hop; ///< Send via this next hop address
+ uint8_t state; ///< State of this route, one of RouteState
+ } RoutingTableEntry;
+
+ /// Constructor.
+ /// \param[in] driver The RadioHead driver to use to transport messages.
+ /// \param[in] thisAddress The address to assign to this node. Defaults to 0
+ RHRouter(RHGenericDriver& driver, uint8_t thisAddress = 0);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// Overrides the init() function in RH.
+ /// Sets max_hops to the default of RH_DEFAULT_MAX_HOPS (30)
+ bool init();
+
+ /// Sets the max_hops to the given value
+ /// This controls the maximum number of hops allowed between source and destination nodes
+ /// Messages that are not delivered by the time their HOPS field exceeds max_hops on a
+ /// routing node will be dropped and ignored.
+ /// \param [in] max_hops The new value for max_hops
+ void setMaxHops(uint8_t max_hops);
+
+ /// Adds a route to the local routing table, or updates it if already present.
+ /// If there is not enough room the oldest (first) route will be deleted by calling retireOldestRoute().
+ /// \param [in] dest The destination node address. RH_BROADCAST_ADDRESS is permitted.
+ /// \param [in] next_hop The address of the next hop to send messages destined for dest
+ /// \param [in] state The satte of the route. Defaults to Valid
+ void addRouteTo(uint8_t dest, uint8_t next_hop, uint8_t state = Valid);
+
+ /// Finds and returns a RoutingTableEntry for the given destination node
+ /// \param [in] dest The desired destination node address.
+ /// \return pointer to a RoutingTableEntry for dest
+ RoutingTableEntry* getRouteTo(uint8_t dest);
+
+ /// Deletes from the local routing table any route for the destination node.
+ /// \param [in] dest The destination node address
+ /// \return true if the route was present
+ bool deleteRouteTo(uint8_t dest);
+
+ /// Deletes the oldest (first) route from the
+ /// local routing table
+ void retireOldestRoute();
+
+ /// Clears all entries from the
+ /// local routing table
+ void clearRoutingTable();
+
+ /// If RH_HAVE_SERIAL is defined, this will print out the contents of the local
+ /// routing table using Serial
+ void printRoutingTable();
+
+ /// Sends a message to the destination node. Initialises the RHRouter message header
+ /// (the SOURCE address is set to the address of this node, HOPS to 0) and calls
+ /// route() which looks up in the routing table the next hop to deliver to and sends the
+ /// message to the next hop. Waits for an acknowledgement from the next hop
+ /// (but not from the destination node (if that is different).
+ /// \param [in] buf The application message data
+ /// \param [in] len Number of octets in the application message data. 0 is permitted
+ /// \param [in] dest The destination node address
+ /// \param [in] flags Optional flags for use by subclasses or application layer,
+ /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck().
+ /// \return The result code:
+ /// - RH_ROUTER_ERROR_NONE Message was routed and delivered to the next hop
+ /// (not necessarily to the final dest address)
+ /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table
+ /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Not able to deliver to the next hop
+ /// (usually because it dod not acknowledge due to being off the air or out of range
+ uint8_t sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags = 0);
+
+ /// Similar to sendtoWait() above, but spoofs the source address.
+ /// For internal use only during routing
+ /// \param [in] buf The application message data.
+ /// \param [in] len Number of octets in the application message data. 0 is permitted.
+ /// \param [in] dest The destination node address.
+ /// \param [in] source The (fake) originating node address.
+ /// \param [in] flags Optional flags for use by subclasses or application layer,
+ /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck().
+ /// \return The result code:
+ /// - RH_ROUTER_ERROR_NONE Message was routed and deliverd to the next hop
+ /// (not necessarily to the final dest address)
+ /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table
+ /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Noyt able to deliver to the next hop
+ /// (usually because it dod not acknowledge due to being off the air or out of range
+ uint8_t sendtoFromSourceWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t source, uint8_t flags = 0);
+
+ /// Starts the receiver if it is not running already.
+ /// If there is a valid message available for this node (or RH_BROADCAST_ADDRESS),
+ /// send an acknowledgement to the last hop
+ /// address (blocking until this is complete), then copy the application message payload data
+ /// to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length..
+ /// If from is not NULL, the originator SOURCE address is placed in *source.
+ /// If to is not NULL, the DEST address is placed in *dest. This might be this nodes address or
+ /// RH_BROADCAST_ADDRESS.
+ /// This is the preferred function for getting messages addressed to this node.
+ /// If the message is not a broadcast, acknowledge to the sender before returning.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address
+ /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was recvived for this node copied to buf
+ bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+ /// Starts the receiver if it is not running already.
+ /// Similar to recvfromAck(), this will block until either a valid message available for this node
+ /// or the timeout expires.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
+ /// \param[in] timeout Maximum time to wait in milliseconds
+ /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address
+ /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address
+ /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
+ /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
+ /// (not just those addressed to this node).
+ /// \return true if a valid message was copied to buf
+ bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
+
+protected:
+
+ /// Lets sublasses peek at messages going
+ /// past before routing or local delivery.
+ /// Called by recvfromAck() immediately after it gets the message from RHReliableDatagram
+ /// \param [in] message Pointer to the RHRouter message that was received.
+ /// \param [in] messageLen Length of message in octets
+ virtual void peekAtMessage(RoutedMessage* message, uint8_t messageLen);
+
+ /// Finds the next-hop route and sends the message via RHReliableDatagram::sendtoWait().
+ /// This is virtual, which lets subclasses override or intercept the route() function.
+ /// Called by sendtoWait after the message header has been filled in.
+ /// \param [in] message Pointer to the RHRouter message to be sent.
+ /// \param [in] messageLen Length of message in octets
+ virtual uint8_t route(RoutedMessage* message, uint8_t messageLen);
+
+ /// Deletes a specific rout entry from therouting table
+ /// \param [in] index The 0 based index of the routing table entry to delete
+ void deleteRoute(uint8_t index);
+
+ /// The last end-to-end sequence number to be used
+ /// Defaults to 0
+ uint8_t _lastE2ESequenceNumber;
+
+ /// The maximum number of hops permitted in routed messages.
+ /// If a routed message would exceed this number of hops it is dropped and ignored.
+ uint8_t _max_hops;
+
+private:
+
+ /// Temporary mesage buffer
+ static RoutedMessage _tmpMessage;
+
+ /// Local routing table
+ RoutingTableEntry _routes[RH_ROUTING_TABLE_SIZE];
+};
+
+/// @example rf22_router_client.pde
+/// @example rf22_router_server1.pde
+/// @example rf22_router_server2.pde
+/// @example rf22_router_server3.pde
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHSPIDriver.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,83 @@
+// RHSPIDriver.cpp
+//
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHSPIDriver.cpp,v 1.9 2014/05/03 00:20:36 mikem Exp $
+
+#include <RHSPIDriver.h>
+
+RHSPIDriver::RHSPIDriver(PINS slaveSelectPin, RHGenericSPI& spi)
+ :
+ _spi(spi),
+ _slaveSelectPin(slaveSelectPin)
+{
+}
+
+bool RHSPIDriver::init()
+{
+ // start the SPI library with the default speeds etc:
+ // On Arduino Due this defaults to SPI1 on the central group of 6 SPI pins
+ _spi.begin();
+
+ // Initialise the slave select pin
+ // On Maple, this must be _after_ spi.begin
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_slaveSelectPin, OUTPUT);
+#endif
+ digitalWrite(_slaveSelectPin, HIGH);
+
+ delay(100);
+ return true;
+}
+
+uint8_t RHSPIDriver::spiRead(uint8_t reg)
+{
+ uint8_t val;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(reg & ~RH_SPI_WRITE_MASK); // Send the address with the write mask off
+ val = _spi.transfer(0); // The written value is ignored, reg value is read
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return val;
+}
+
+uint8_t RHSPIDriver::spiWrite(uint8_t reg, uint8_t val)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the address with the write mask on
+ _spi.transfer(val); // New value follows
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+uint8_t RHSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg & ~RH_SPI_WRITE_MASK); // Send the start address with the write mask off
+ while (len--)
+ *dest++ = _spi.transfer(0);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+uint8_t RHSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len)
+{
+ uint8_t status = 0;
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the start address with the write mask on
+ while (len--)
+ _spi.transfer(*src++);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return status;
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHSPIDriver.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,92 @@
+// RHSPIDriver.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHSPIDriver.h,v 1.9 2014/04/23 06:00:59 mikem Exp $
+
+#ifndef RHSPIDriver_h
+#define RHSPIDriver_h
+
+#include <RHGenericDriver.h>
+#include <RHHardwareSPI.h>
+
+// This is the bit in the SPI address that marks it as a write
+#define RH_SPI_WRITE_MASK 0x80
+
+class RHGenericSPI;
+
+/////////////////////////////////////////////////////////////////////
+/// \class RHSPIDriver RHSPIDriver.h <RHSPIDriver.h>
+/// \brief Base class for a RadioHead drivers that use the SPI bus
+/// to communicate with its transport hardware.
+///
+/// This class can be subclassed by Drivers that require to use the SPI bus.
+/// It can be configured to use either the RHHardwareSPI class (if there is one available on the platform)
+/// of the bitbanged RHSoftwareSPI class. The default behaviour is to use a pre-instantiated built-in RHHardwareSPI
+/// interface.
+///
+/// SPI bus access is protected by ATOMIC_BLOCK_START and ATOMIC_BLOCK_END, which will ensure interrupts
+/// are disabled during access.
+///
+/// The read and write routines implement commonly used SPI conventions: specifically that the MSB
+/// of the first byte transmitted indicates that it is a write and the remaining bits indicate the rehgister to access)
+/// This can be overriden
+/// in subclasses if necessaryor an alternative class, RHNRFSPIDriver can be used to access devices like
+/// Nordic NRF series radios, which have different requirements.
+///
+/// Application developers are not expected to instantiate this class directly:
+/// it is for the use of Driver developers.
+class RHSPIDriver : public RHGenericDriver
+{
+public:
+ /// Constructor
+ /// \param[in] slaveSelectPin The controler pin to use to select the desired SPI device. This pin will be driven LOW
+ /// during SPI communications with the SPI device that uis iused by this Driver.
+ /// \param[in] spi Reference to the SPI interface to use. The default is to use a default built-in Hardware interface.
+ RHSPIDriver(PINS slaveSelectPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialise the Driver transport hardware and software.
+ /// Make sure the Driver is properly configured before calling init().
+ /// \return true if initialisation succeeded.
+ bool init();
+
+ /// Reads a single register from the SPI device
+ /// \param[in] reg Register number
+ /// \return The value of the register
+ uint8_t spiRead(uint8_t reg);
+
+ /// Writes a single byte to the SPI device
+ /// \param[in] reg Register number
+ /// \param[in] val The value to write
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiWrite(uint8_t reg, uint8_t val);
+
+ /// Reads a number of consecutive registers from the SPI device using burst read mode
+ /// \param[in] reg Register number of the first register
+ /// \param[in] dest Array to write the register values to. Must be at least len bytes
+ /// \param[in] len Number of bytes to read
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len);
+
+ /// Write a number of consecutive registers using burst write mode
+ /// \param[in] reg Register number of the first register
+ /// \param[in] src Array of new register values to write. Must be at least len bytes
+ /// \param[in] len Number of bytes to write
+ /// \return Some devices return a status byte during the first data transfer. This byte is returned.
+ /// it may or may not be meaningfule depending on the the type of device being accessed.
+ uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len);
+
+protected:
+ /// Reference to the RHGenericSPI instance to use to trasnfer data with teh SPI device
+ RHGenericSPI& _spi;
+
+ /// The pin number of the Slave Selct pin that is used to select the desired device.
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ DigitalOut _slaveSelectPin;
+#else
+ uint8_t _slaveSelectPin;
+#endif
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHTcpProtocol.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,66 @@
+// RH_TcpProtocol.h
+// Author: Mike McCauley (mikem@aierspayce.com)
+// Definition of protocol messages sent and received by RH_TCP
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHTcpProtocol.h,v 1.3 2014/05/22 06:07:09 mikem Exp $
+
+/// This file contains the definitions of message structures passed between
+/// RH_TCP and the etherSimulator
+#ifndef RH_TcpProtocol_h
+#define RH_TcpProtocol_h
+
+#define RH_TCP_MESSAGE_TYPE_NOP 0
+#define RH_TCP_MESSAGE_TYPE_THISADDRESS 1
+#define RH_TCP_MESSAGE_TYPE_PACKET 2
+
+// Maximum message length (including the headers) we are willing to support
+#define RH_TCP_MAX_PAYLOAD_LEN 255
+
+// The length of the headers we add.
+// The headers are inside the RF69's payload and are therefore encrypted if encryption is enabled
+#define RH_TCP_HEADER_LEN 4
+
+
+// This is the maximum message length that can be supported by this protocol.
+#define RH_TCP_MAX_MESSAGE_LEN (RH_TCP_MAX_PAYLOAD_LEN - RH_TCP_HEADER_LEN)
+
+#pragma pack(push, 1) // No padding
+
+/// \brief Generic RH_TCP simulator message structure
+typedef struct
+{
+ uint32_t length; ///< Number of octets following, in network byte order
+ uint8_t payload[RH_TCP_MAX_PAYLOAD_LEN + 1]; ///< Payload
+} RHTcpMessage;
+
+/// \brief Generic RH_TCP message structure with message type
+typedef struct
+{
+ uint32_t length; ///< Number of octets following, in network byte order
+ uint8_t type; ///< One of RH_TCP_MESSAGE_TYPE_*
+ uint8_t payload[RH_TCP_MAX_PAYLOAD_LEN]; ///< Payload
+} RHTcpTypeMessage;
+
+/// \brief RH_TCP message Notifies the server of thisAddress of this client
+typedef struct
+{
+ uint32_t length; ///< Number of octets following, in network byte order
+ uint8_t type; ///< == RH_TCP_MESSAGE_TYPE_THISADDRESS
+ uint8_t thisAddress; ///< Node address
+} RHTcpThisAddress;
+
+/// \brief RH_TCP radio message passed to or from the simulator
+typedef struct
+{
+ uint32_t length; ///< Number of octets following, in network byte order
+ uint8_t type; ///< == RH_TCP_MESSAGE_TYPE_PACKET
+ uint8_t to; ///< Node address of the recipient
+ uint8_t from; ///< Node address of the sender
+ uint8_t id; ///< Message sequence number
+ uint8_t flags; ///< Message flags
+ uint8_t payload[RH_TCP_MAX_MESSAGE_LEN]; ///< 0 or more, length deduced from length above
+} RHTcpPacket;
+
+#pragma pack(pop)
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF24.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,338 @@
+// NRF24.cpp
+//
+// Copyright (C) 2012 Mike McCauley
+// $Id: RH_NRF24.cpp,v 1.21 2015/03/29 03:53:47 mikem Exp $
+
+#include <RH_NRF24.h>
+
+RH_NRF24::RH_NRF24(PINS chipEnablePin, PINS slaveSelectPin, RHGenericSPI& spi)
+ :
+ RHNRFSPIDriver(slaveSelectPin, spi),
+ _rxBufValid(0)
+{
+ _configuration = RH_NRF24_EN_CRC | RH_NRF24_CRCO; // Default: 2 byte CRC enabled
+ _chipEnablePin = chipEnablePin;
+}
+
+bool RH_NRF24::init()
+{
+ // Teensy with nRF24 is unreliable at 8MHz:
+ // so is Arduino with RF73
+ _spi.setFrequency(RHGenericSPI::Frequency1MHz);
+ if (!RHNRFSPIDriver::init())
+ return false;
+
+ // Initialise the slave select pin
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_chipEnablePin, OUTPUT);
+#endif
+ digitalWrite(_chipEnablePin, LOW);
+
+ // Clear interrupts
+ spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR | RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
+ // Enable dynamic payload length on all pipes
+ spiWriteRegister(RH_NRF24_REG_1C_DYNPD, RH_NRF24_DPL_ALL);
+ // Enable dynamic payload length, disable payload-with-ack, enable noack
+ spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
+ // Test if there is actually a device connected and responding
+ // CAUTION: RFM73 and version 2.0 silicon may require ACTIVATE
+ if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
+ {
+ spiWrite(RH_NRF24_COMMAND_ACTIVATE, 0x73);
+ // Enable dynamic payload length, disable payload-with-ack, enable noack
+ spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
+ if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
+ return false;
+ }
+
+ // Make sure we are powered down
+ setModeIdle();
+
+ // Flush FIFOs
+ flushTx();
+ flushRx();
+
+ setChannel(2); // The default, in case it was set by another app without powering down
+ setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm);
+
+ return true;
+}
+
+// Use the register commands to read and write the registers
+uint8_t RH_NRF24::spiReadRegister(uint8_t reg)
+{
+ return spiRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER);
+}
+
+uint8_t RH_NRF24::spiWriteRegister(uint8_t reg, uint8_t val)
+{
+ return spiWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, val);
+}
+
+uint8_t RH_NRF24::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len)
+{
+ return spiBurstRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER, dest, len);
+}
+
+uint8_t RH_NRF24::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len)
+{
+ return spiBurstWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, src, len);
+}
+
+uint8_t RH_NRF24::statusRead()
+{
+ // status is a side-effect of NOP, faster than reading reg 07
+ return spiCommand(RH_NRF24_COMMAND_NOP);
+}
+
+uint8_t RH_NRF24::flushTx()
+{
+ return spiCommand(RH_NRF24_COMMAND_FLUSH_TX);
+}
+
+uint8_t RH_NRF24::flushRx()
+{
+ return spiCommand(RH_NRF24_COMMAND_FLUSH_RX);
+}
+
+bool RH_NRF24::setChannel(uint8_t channel)
+{
+ spiWriteRegister(RH_NRF24_REG_05_RF_CH, channel & RH_NRF24_RF_CH);
+ return true;
+}
+
+bool RH_NRF24::setOpMode(uint8_t mode)
+{
+ _configuration = mode;
+ return true;
+}
+
+bool RH_NRF24::setNetworkAddress(uint8_t* address, uint8_t len)
+{
+ if (len < 3 || len > 5)
+ return false;
+
+ // Set both TX_ADDR and RX_ADDR_P0 for auto-ack with Enhanced shockwave
+ spiWriteRegister(RH_NRF24_REG_03_SETUP_AW, len-2); // Mapping [3..5] = [1..3]
+ spiBurstWriteRegister(RH_NRF24_REG_0A_RX_ADDR_P0, address, len);
+ spiBurstWriteRegister(RH_NRF24_REG_10_TX_ADDR, address, len);
+ return true;
+}
+
+bool RH_NRF24::setRF(DataRate data_rate, TransmitPower power)
+{
+ uint8_t value = (power << 1) & RH_NRF24_PWR;
+ // Ugly mapping of data rates to noncontiguous 2 bits:
+ if (data_rate == DataRate250kbps)
+ value |= RH_NRF24_RF_DR_LOW;
+ else if (data_rate == DataRate2Mbps)
+ value |= RH_NRF24_RF_DR_HIGH;
+ // else DataRate1Mbps, 00
+
+ // RFM73 needs this:
+ value |= RH_NRF24_LNA_HCURR;
+
+ spiWriteRegister(RH_NRF24_REG_06_RF_SETUP, value);
+ // If we were using auto-ack, we would have to set the appropriate timeout in reg 4 here
+ // see NRF24::setRF()
+ return true;
+}
+
+void RH_NRF24::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration);
+ digitalWrite(_chipEnablePin, LOW);
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_NRF24::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ spiWriteRegister(RH_NRF24_REG_00_CONFIG, 0); // Power Down mode
+ digitalWrite(_chipEnablePin, LOW);
+ _mode = RHModeSleep;
+ }
+}
+
+void RH_NRF24::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP | RH_NRF24_PRIM_RX);
+ digitalWrite(_chipEnablePin, HIGH);
+ _mode = RHModeRx;
+ }
+}
+
+void RH_NRF24::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ // Its the CE rising edge that puts us into TX mode
+ // CE staying high makes us go to standby-II when the packet is sent
+ digitalWrite(_chipEnablePin, LOW);
+ // Ensure DS is not set
+ spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
+ spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP);
+ digitalWrite(_chipEnablePin, HIGH);
+ _mode = RHModeTx;
+ }
+}
+
+bool RH_NRF24::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_NRF24_MAX_MESSAGE_LEN)
+ return false;
+ // Set up the headers
+ _buf[0] = _txHeaderTo;
+ _buf[1] = _txHeaderFrom;
+ _buf[2] = _txHeaderId;
+ _buf[3] = _txHeaderFlags;
+ memcpy(_buf+RH_NRF24_HEADER_LEN, data, len);
+ spiBurstWrite(RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK, _buf, len + RH_NRF24_HEADER_LEN);
+ setModeTx();
+ // Radio will return to Standby II mode after transmission is complete
+ _txGood++;
+ return true;
+}
+
+bool RH_NRF24::waitPacketSent()
+{
+ // If we are not currently in transmit mode, there is no packet to wait for
+ if (_mode != RHModeTx)
+ return false;
+
+ // Wait for either the Data Sent or Max ReTries flag, signalling the
+ // end of transmission
+ // We dont actually use auto-ack, so prob dont expect to see RH_NRF24_MAX_RT
+ uint8_t status;
+ while (!((status = statusRead()) & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT)))
+ YIELD;
+
+ // Must clear RH_NRF24_MAX_RT if it is set, else no further comm
+ if (status & RH_NRF24_MAX_RT)
+ flushTx();
+ setModeIdle();
+ spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
+ // Return true if data sent, false if MAX_RT
+ return status & RH_NRF24_TX_DS;
+}
+
+bool RH_NRF24::isSending()
+{
+ return !(spiReadRegister(RH_NRF24_REG_00_CONFIG) & RH_NRF24_PRIM_RX) &&
+ !(statusRead() & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT));
+}
+
+bool RH_NRF24::printRegisters()
+{
+#ifdef RH_HAVE_SERIAL
+ // Iterate over register range, but don't process registers not in use.
+ for (uint8_t r = RH_NRF24_REG_00_CONFIG; r <= RH_NRF24_REG_1D_FEATURE; r++)
+ {
+ if ((r <= RH_NRF24_REG_17_FIFO_STATUS) || (r >= RH_NRF24_REG_1C_DYNPD))
+ {
+ Serial.print(r, HEX);
+ Serial.print(": ");
+ uint8_t len = 1;
+ // Address registers are 5 bytes in size
+ if ( (RH_NRF24_REG_0A_RX_ADDR_P0 == r)
+ || (RH_NRF24_REG_0B_RX_ADDR_P1 == r)
+ || (RH_NRF24_REG_10_TX_ADDR == r) )
+ {
+ len = 5;
+ }
+ uint8_t buf[5];
+ spiBurstReadRegister(r, buf, len);
+ for (uint8_t j = 0; j < len; ++j)
+ {
+ Serial.print(buf[j], HEX);
+ Serial.print(" ");
+ }
+ Serial.println("");
+ }
+ }
+#endif
+
+ return true;
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_NRF24::validateRxBuf()
+{
+ if (_bufLen < 4)
+ return; // Too short to be a real message
+ // Extract the 4 headers
+ _rxHeaderTo = _buf[0];
+ _rxHeaderFrom = _buf[1];
+ _rxHeaderId = _buf[2];
+ _rxHeaderFlags = _buf[3];
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ _rxGood++;
+ _rxBufValid = true;
+ }
+}
+
+bool RH_NRF24::available()
+{
+ if (!_rxBufValid)
+ {
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx();
+ if (spiReadRegister(RH_NRF24_REG_17_FIFO_STATUS) & RH_NRF24_RX_EMPTY)
+ return false;
+ // Manual says that messages > 32 octets should be discarded
+ uint8_t len = spiRead(RH_NRF24_COMMAND_R_RX_PL_WID);
+ if (len > 32)
+ {
+ flushRx();
+ clearRxBuf();
+ setModeIdle();
+ return false;
+ }
+ // Clear read interrupt
+ spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR);
+ // Get the message into the RX buffer, so we can inspect the headers
+ spiBurstRead(RH_NRF24_COMMAND_R_RX_PAYLOAD, _buf, len);
+ _bufLen = len;
+ // 140 microsecs (32 octet payload)
+ validateRxBuf();
+ if (_rxBufValid)
+ setModeIdle(); // Got one
+ }
+ return _rxBufValid;
+}
+
+void RH_NRF24::clearRxBuf()
+{
+ _rxBufValid = false;
+ _bufLen = 0;
+}
+
+bool RH_NRF24::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+ if (buf && len)
+ {
+ // Skip the 4 headers that are at the beginning of the rxBuf
+ if (*len > _bufLen-RH_NRF24_HEADER_LEN)
+ *len = _bufLen-RH_NRF24_HEADER_LEN;
+ memcpy(buf, _buf+RH_NRF24_HEADER_LEN, *len);
+ }
+ clearRxBuf(); // This message accepted and cleared
+ return true;
+}
+
+uint8_t RH_NRF24::maxMessageLength()
+{
+ return RH_NRF24_MAX_MESSAGE_LEN;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF24.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,623 @@
+// RH_NRF24.h
+// Author: Mike McCauley
+// Copyright (C) 2012 Mike McCauley
+// $Id: RH_NRF24.h,v 1.16 2015/08/13 02:45:47 mikem Exp $
+//
+
+#ifndef RH_NRF24_h
+#define RH_NRF24_h
+
+#include <RHGenericSPI.h>
+#include <RHNRFSPIDriver.h>
+
+// This is the maximum number of bytes that can be carried by the nRF24.
+// We use some for headers, keeping fewer for RadioHead messages
+#define RH_NRF24_MAX_PAYLOAD_LEN 32
+
+// The length of the headers we add.
+// The headers are inside the nRF24 payload
+#define RH_NRF24_HEADER_LEN 4
+
+// This is the maximum RadioHead user message length that can be supported by this library. Limited by
+// the supported message lengths in the nRF24
+#define RH_NRF24_MAX_MESSAGE_LEN (RH_NRF24_MAX_PAYLOAD_LEN-RH_NRF24_HEADER_LEN)
+
+// SPI Command names
+#define RH_NRF24_COMMAND_R_REGISTER 0x00
+#define RH_NRF24_COMMAND_W_REGISTER 0x20
+#define RH_NRF24_COMMAND_ACTIVATE 0x50 // only on RFM73 ?
+#define RH_NRF24_COMMAND_R_RX_PAYLOAD 0x61
+#define RH_NRF24_COMMAND_W_TX_PAYLOAD 0xa0
+#define RH_NRF24_COMMAND_FLUSH_TX 0xe1
+#define RH_NRF24_COMMAND_FLUSH_RX 0xe2
+#define RH_NRF24_COMMAND_REUSE_TX_PL 0xe3
+#define RH_NRF24_COMMAND_R_RX_PL_WID 0x60
+#define RH_NRF24_COMMAND_W_ACK_PAYLOAD(pipe) (0xa8|(pipe&0x7))
+#define RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK 0xb0
+#define RH_NRF24_COMMAND_NOP 0xff
+
+// Register names
+#define RH_NRF24_REGISTER_MASK 0x1f
+#define RH_NRF24_REG_00_CONFIG 0x00
+#define RH_NRF24_REG_01_EN_AA 0x01
+#define RH_NRF24_REG_02_EN_RXADDR 0x02
+#define RH_NRF24_REG_03_SETUP_AW 0x03
+#define RH_NRF24_REG_04_SETUP_RETR 0x04
+#define RH_NRF24_REG_05_RF_CH 0x05
+#define RH_NRF24_REG_06_RF_SETUP 0x06
+#define RH_NRF24_REG_07_STATUS 0x07
+#define RH_NRF24_REG_08_OBSERVE_TX 0x08
+#define RH_NRF24_REG_09_RPD 0x09
+#define RH_NRF24_REG_0A_RX_ADDR_P0 0x0a
+#define RH_NRF24_REG_0B_RX_ADDR_P1 0x0b
+#define RH_NRF24_REG_0C_RX_ADDR_P2 0x0c
+#define RH_NRF24_REG_0D_RX_ADDR_P3 0x0d
+#define RH_NRF24_REG_0E_RX_ADDR_P4 0x0e
+#define RH_NRF24_REG_0F_RX_ADDR_P5 0x0f
+#define RH_NRF24_REG_10_TX_ADDR 0x10
+#define RH_NRF24_REG_11_RX_PW_P0 0x11
+#define RH_NRF24_REG_12_RX_PW_P1 0x12
+#define RH_NRF24_REG_13_RX_PW_P2 0x13
+#define RH_NRF24_REG_14_RX_PW_P3 0x14
+#define RH_NRF24_REG_15_RX_PW_P4 0x15
+#define RH_NRF24_REG_16_RX_PW_P5 0x16
+#define RH_NRF24_REG_17_FIFO_STATUS 0x17
+#define RH_NRF24_REG_1C_DYNPD 0x1c
+#define RH_NRF24_REG_1D_FEATURE 0x1d
+
+// These register masks etc are named wherever possible
+// corresponding to the bit and field names in the nRF24L01 Product Specification
+// #define RH_NRF24_REG_00_CONFIG 0x00
+#define RH_NRF24_MASK_RX_DR 0x40
+#define RH_NRF24_MASK_TX_DS 0x20
+#define RH_NRF24_MASK_MAX_RT 0x10
+#define RH_NRF24_EN_CRC 0x08
+#define RH_NRF24_CRCO 0x04
+#define RH_NRF24_PWR_UP 0x02
+#define RH_NRF24_PRIM_RX 0x01
+
+// #define RH_NRF24_REG_01_EN_AA 0x01
+#define RH_NRF24_ENAA_P5 0x20
+#define RH_NRF24_ENAA_P4 0x10
+#define RH_NRF24_ENAA_P3 0x08
+#define RH_NRF24_ENAA_P2 0x04
+#define RH_NRF24_ENAA_P1 0x02
+#define RH_NRF24_ENAA_P0 0x01
+
+// #define RH_NRF24_REG_02_EN_RXADDR 0x02
+#define RH_NRF24_ERX_P5 0x20
+#define RH_NRF24_ERX_P4 0x10
+#define RH_NRF24_ERX_P3 0x08
+#define RH_NRF24_ERX_P2 0x04
+#define RH_NRF24_ERX_P1 0x02
+#define RH_NRF24_ERX_P0 0x01
+
+// #define RH_NRF24_REG_03_SETUP_AW 0x03
+#define RH_NRF24_AW_3_BYTES 0x01
+#define RH_NRF24_AW_4_BYTES 0x02
+#define RH_NRF24_AW_5_BYTES 0x03
+
+// #define RH_NRF24_REG_04_SETUP_RETR 0x04
+#define RH_NRF24_ARD 0xf0
+#define RH_NRF24_ARC 0x0f
+
+// #define RH_NRF24_REG_05_RF_CH 0x05
+#define RH_NRF24_RF_CH 0x7f
+
+// #define RH_NRF24_REG_06_RF_SETUP 0x06
+#define RH_NRF24_CONT_WAVE 0x80
+#define RH_NRF24_RF_DR_LOW 0x20
+#define RH_NRF24_PLL_LOCK 0x10
+#define RH_NRF24_RF_DR_HIGH 0x08
+#define RH_NRF24_PWR 0x06
+#define RH_NRF24_PWR_m18dBm 0x00
+#define RH_NRF24_PWR_m12dBm 0x02
+#define RH_NRF24_PWR_m6dBm 0x04
+#define RH_NRF24_PWR_0dBm 0x06
+#define RH_NRF24_LNA_HCURR 0x01
+
+// #define RH_NRF24_REG_07_STATUS 0x07
+#define RH_NRF24_RX_DR 0x40
+#define RH_NRF24_TX_DS 0x20
+#define RH_NRF24_MAX_RT 0x10
+#define RH_NRF24_RX_P_NO 0x0e
+#define RH_NRF24_STATUS_TX_FULL 0x01
+
+// #define RH_NRF24_REG_08_OBSERVE_TX 0x08
+#define RH_NRF24_PLOS_CNT 0xf0
+#define RH_NRF24_ARC_CNT 0x0f
+
+// #define RH_NRF24_REG_09_RPD 0x09
+#define RH_NRF24_RPD 0x01
+
+// #define RH_NRF24_REG_17_FIFO_STATUS 0x17
+#define RH_NRF24_TX_REUSE 0x40
+#define RH_NRF24_TX_FULL 0x20
+#define RH_NRF24_TX_EMPTY 0x10
+#define RH_NRF24_RX_FULL 0x02
+#define RH_NRF24_RX_EMPTY 0x01
+
+// #define RH_NRF24_REG_1C_DYNPD 0x1c
+#define RH_NRF24_DPL_ALL 0x3f
+#define RH_NRF24_DPL_P5 0x20
+#define RH_NRF24_DPL_P4 0x10
+#define RH_NRF24_DPL_P3 0x08
+#define RH_NRF24_DPL_P2 0x04
+#define RH_NRF24_DPL_P1 0x02
+#define RH_NRF24_DPL_P0 0x01
+
+// #define RH_NRF24_REG_1D_FEATURE 0x1d
+#define RH_NRF24_EN_DPL 0x04
+#define RH_NRF24_EN_ACK_PAY 0x02
+#define RH_NRF24_EN_DYN_ACK 0x01
+
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_NRF24 RH_NRF24.h <RH_NRF24.h>
+/// \brief Send and receive addressed, reliable, acknowledged datagrams by nRF24L01 and compatible transceivers.
+///
+/// Supported transceivers include:
+/// - Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01
+/// and other compatible transceivers.
+/// - nRF24L01p with PA and LNA modules that produce a higher power output similar to this one:
+/// http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA
+/// - Sparkfun WRL-00691 module with nRF24L01 https://www.sparkfun.com/products/691
+/// or WRL-00705 https://www.sparkfun.com/products/705 etc.
+/// - Hope-RF RFM73 http://www.hoperf.com/rf/2.4g_module/RFM73.htm and
+/// http://www.anarduino.com/details.jsp?pid=121
+/// and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate
+/// with each other.
+///
+/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams
+/// of arbitrary length to 28 octets per packet. Use one of the Manager classes to get addressing and
+/// acknowledgement reliability, routing, meshes etc.
+///
+/// The nRF24L01 (http://www.sparkfun.com/datasheets/Wireless/Nordic/nRF24L01P_Product_Specification_1_0.pdf)
+/// is a low-cost 2.4GHz ISM transceiver module. It supports a number of channel frequencies in the 2.4GHz band
+/// and a range of data rates.
+///
+/// This library provides functions for sending and receiving messages of up to 28 octets on any
+/// frequency supported by the nRF24L01, at a selected data rate.
+///
+/// Several nRF24L01 modules can be connected to an Arduino, permitting the construction of translators
+/// and frequency changers, etc.
+///
+/// The nRF24 transceiver is configured to use Enhanced Shockburst with no acknowledgement and no retransmits.
+/// TX_ADDR and RX_ADDR_P0 are set to the network address. If you need the low level auto-acknowledgement
+/// feature supported by this chip, you can use our original NRF24 library
+/// at http://www.airspayce.com/mikem/arduino/NRF24
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// data rate, and with identical network addresses.
+///
+/// Example Arduino programs are included to show the main modes of use.
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this class conform to this packet format, as specified by
+/// the nRF24L01 product specification:
+///
+/// - 1 octets PREAMBLE
+/// - 3 to 5 octets NETWORK ADDRESS
+/// - 9 bits packet control field
+/// - 0 to 32 octets PAYLOAD, consisting of:
+/// - 1 octet TO header
+/// - 1 octet FROM header
+/// - 1 octet ID header
+/// - 1 octet FLAGS header
+/// - 0 to 28 octets of user message
+/// - 2 octets CRC
+///
+/// \par Connecting nRF24L01 to Arduino
+///
+/// The electrical connection between the nRF24L01 and the Arduino require 3.3V, the 3 x SPI pins (SCK, SDI, SDO),
+/// a Chip Enable pin and a Slave Select pin.
+/// If you are using the Sparkfun WRL-00691 module, it has a voltage regulator on board and
+/// can be should with 5V VCC if possible.
+/// The examples below assume the Sparkfun WRL-00691 module
+///
+/// Connect the nRF24L01 to most Arduino's like this (Caution, Arduino Mega has different pins for SPI,
+/// see below). Use these same connections for Teensy 3.1 (use 3.3V not 5V Vcc).
+/// \code
+/// Arduino Sparkfun WRL-00691
+/// 5V-----------VCC (3.3V to 7V in)
+/// pin D8-----------CE (chip enable in)
+/// SS pin D10----------CSN (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND----------GND (ground in)
+/// \endcode
+///
+/// For an Arduino Leonardo (the SPI pins do not come out on the Digital pins as for normal Arduino, but only
+/// appear on the ICSP header)
+/// \code
+/// Leonardo Sparkfun WRL-00691
+/// 5V-----------VCC (3.3V to 7V in)
+/// pin D8-----------CE (chip enable in)
+/// SS pin D10----------CSN (chip select in)
+/// SCK ICSP pin 3----------SCK (SPI clock in)
+/// MOSI ICSP pin 4----------SDI (SPI Data in)
+/// MISO ICSP pin 1----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND----------GND (ground in)
+/// \endcode
+/// and initialise the NRF24 object like this to explicitly set the SS pin
+/// NRF24 nrf24(8, 10);
+///
+/// For an Arduino Mega:
+/// \code
+/// Mega Sparkfun WRL-00691
+/// 5V-----------VCC (3.3V to 7V in)
+/// pin D8-----------CE (chip enable in)
+/// SS pin D53----------CSN (chip select in)
+/// SCK pin D52----------SCK (SPI clock in)
+/// MOSI pin D51----------SDI (SPI Data in)
+/// MISO pin D50----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND----------GND (ground in)
+/// \endcode
+/// and you can then use the constructor RH_NRF24(8, 53).
+///
+/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by hardware SPI to the
+/// ITDB02 Parallel LCD Module Interface pins:
+/// \code
+/// IBoard Signal=ITDB02 pin Sparkfun WRL-00691
+/// 3.3V 37-----------VCC (3.3V to 7V in)
+/// D2 28-----------CE (chip enable in)
+/// D29 27----------CSN (chip select in)
+/// SCK D52 32----------SCK (SPI clock in)
+/// MOSI D51 34----------SDI (SPI Data in)
+/// MISO D50 30----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND 39----------GND (ground in)
+/// \endcode
+/// And initialise like this:
+/// \code
+/// RH_NRF24 nrf24(2, 29);
+/// \endcode
+///
+/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by software SPI to the
+/// nRF24L01+ Module Interface pins. CAUTION: performance of software SPI is very slow and is not
+/// compatible with other modules running hardware SPI.
+/// \code
+/// IBoard Signal=Module pin Sparkfun WRL-00691
+/// 3.3V 2----------VCC (3.3V to 7V in)
+/// D12 3-----------CE (chip enable in)
+/// D29 4----------CSN (chip select in)
+/// D9 5----------SCK (SPI clock in)
+/// D8 6----------SDI (SPI Data in)
+/// D7 7----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND 1----------GND (ground in)
+/// \endcode
+/// And initialise like this:
+/// \code
+/// #include <SPI.h>
+/// #include <RH_NRF24.h>
+/// #include <RHSoftwareSPI.h>
+/// Singleton instance of the radio driver
+/// RHSoftwareSPI spi;
+/// RH_NRF24 nrf24(12, 11, spi);
+/// void setup() {
+/// spi.setPins(7, 8, 9);
+/// ....
+/// \endcode
+///
+///
+/// For Raspberry Pi with Sparkfun WRL-00691
+/// \code
+/// Raspberry Pi P1 pin Sparkfun WRL-00691
+/// 5V 2-----------VCC (3.3V to 7V in)
+/// GPIO25 22-----------CE (chip enable in)
+/// GPIO8 24----------CSN (chip select in)
+/// GPIO11 23----------SCK (SPI clock in)
+/// GPIO10 19----------SDI (SPI Data in)
+/// GPIO9 21----------SDO (SPI data out)
+/// IRQ (Interrupt output, not connected)
+/// GND 6----------GND (ground in)
+/// \endcode
+/// and initialise like this:
+/// \code
+/// RH_NRF24 nrf24(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24);
+/// \endcode
+/// See the example program and Makefile in examples/raspi. Requires bcm2835 library to be previously installed.
+/// \code
+/// cd examples/raspi
+/// make
+/// sudo ./RasPiRH
+/// \endcode
+/// \code
+///
+/// You can override the default settings for the CSN and CE pins
+/// in the NRF24() constructor if you wish to connect the slave select CSN to other than the normal one for your
+/// Arduino (D10 for Diecimila, Uno etc and D53 for Mega)
+///
+/// Caution: on some Arduinos such as the Mega 2560, if you set the slave select pin to be other than the usual SS
+/// pin (D53 on Mega 2560), you may need to set the usual SS pin to be an output to force the Arduino into SPI
+/// master mode.
+///
+/// Caution: this module has not been proved to work with Leonardo, at least without level
+/// shifters between the nRF24 and the Leonardo. Tests seem to indicate that such level shifters would be required
+/// with Leonardo to make it work.
+///
+/// It is possible to have 2 radios conected to one arduino, provided each radio has its own
+/// CSN and CE line (SCK, SDI and SDO are common to both radios)
+///
+/// \par SPI Interface
+///
+/// You can interface to nRF24L01 with with hardware or software SPI. Use of software SPI with the RHSoftwareSPI
+/// class depends on a fast enough processor and digitalOut() functions to achieve a high enough SPI bus frequency.
+/// If you observe reliable behaviour with the default hardware SPI RHHardwareSPI, but unreliable behaviour
+/// with Software SPI RHSoftwareSPI, it may be due to slow CPU performance.
+///
+/// Initialisation example with hardware SPI
+/// \code
+/// #include <RH_NRF24.h>
+/// RH_NRF24 driver;
+/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
+/// \endcode
+///
+/// Initialisation example with software SPI
+/// \code
+/// #include <RH_NRF24.h>
+/// #include <RHSoftwareSPI.h>
+/// RHSoftwareSPI spi;
+/// RH_NRF24 driver(8, 10, spi);
+/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
+/// \endcode
+///
+/// \par Example programs
+///
+/// Several example programs are provided.
+///
+/// \par Radio Performance
+///
+/// Frequency accuracy may be debatable. For nominal frequency of 2401.000 MHz (ie channel 1),
+/// my Yaesu VR-5000 receiver indicated the center frequency for my test radios
+/// was 2401.121 MHz. Its not clear to me if the Yaesu
+/// is the source of the error, but I tend to believe it, which would make the nRF24l01 frequency out by 121kHz.
+///
+/// The measured power output for a nRF24L01p with PA and LNA set to 0dBm output is about 18dBm.
+///
+/// \par Radio operating strategy and defaults
+///
+/// The radio is enabled all the time, and switched between TX and RX modes depending on
+/// whether there is any data to send. Sending data sets the radio to TX mode.
+/// After data is sent, the radio automatically returns to Standby II mode. Calling waitAvailable() or
+/// waitAvailableTimeout() starts the radio in RX mode.
+///
+/// The radio is configured by default to Channel 2, 2Mbps, 0dBm power, 5 bytes address, payload width 1, CRC enabled
+/// 2 byte CRC, No Auto-Ack mode. Enhanced shockburst is used.
+/// TX and P0 are set to the Network address. Node addresses and decoding are handled with the RH_NRF24 module.
+///
+/// \par Memory
+///
+/// Memory usage of this class is minimal. The compiled client and server sketches are about 6000 bytes on Arduino.
+/// The reliable client and server sketches compile to about 8500 bytes on Arduino.
+/// RAM requirements are minimal.
+///
+class RH_NRF24 : public RHNRFSPIDriver
+{
+public:
+
+ /// \brief Defines convenient values for setting data rates in setRF()
+ typedef enum
+ {
+ DataRate1Mbps = 0, ///< 1 Mbps
+ DataRate2Mbps, ///< 2 Mbps
+ DataRate250kbps ///< 250 kbps
+ } DataRate;
+
+ /// \brief Convenient values for setting transmitter power in setRF()
+ /// These are designed to agree with the values for RF_PWR in RH_NRF24_REG_06_RF_SETUP
+ /// To be passed to setRF();
+ typedef enum
+ {
+ // Add 20dBm for nRF24L01p with PA and LNA modules
+ TransmitPowerm18dBm = 0, ///< On nRF24, -18 dBm
+ TransmitPowerm12dBm, ///< On nRF24, -12 dBm
+ TransmitPowerm6dBm, ///< On nRF24, -6 dBm
+ TransmitPower0dBm, ///< On nRF24, 0 dBm
+ // Sigh, different power levels for the same bit patterns on RFM73:
+ // On RFM73P-S, there is a Tx power amp, so expect higher power levels, up to 20dBm. Alas
+ // there is no clear documentation on the power for different settings :-(
+ RFM73TransmitPowerm10dBm = 0, ///< On RFM73, -10 dBm
+ RFM73TransmitPowerm5dBm, ///< On RFM73, -5 dBm
+ RFM73TransmitPowerm0dBm, ///< On RFM73, 0 dBm
+ RFM73TransmitPower5dBm ///< On RFM73, 5 dBm. 20dBm on RFM73P-S2 ?
+
+ } TransmitPower;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// chip enable and slave select pin.
+ /// After constructing, you must call init() to initialise the interface
+ /// and the radio module
+ /// \param[in] chipEnablePin the Arduino pin to use to enable the chip for transmit/receive
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the NRF24 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega,
+ /// D10 for Maple)
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_NRF24(PINS chipEnablePin, PINS slaveSelectPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:g
+ /// - Set the chip enable and chip select pins to output LOW, HIGH respectively.
+ /// - Initialise the SPI output pins
+ /// - Initialise the SPI interface library to 8MHz (Hint, if you want to lower
+ /// the SPI frequency (perhaps where you have other SPI shields, low voltages etc),
+ /// call SPI.setClockDivider() after init()).
+ /// -Flush the receiver and transmitter buffers
+ /// - Set the radio to receive with powerUpRx();
+ /// \return true if everything was successful
+ bool init();
+
+ /// Reads a single register from the NRF24
+ /// \param[in] reg Register number, one of NRF24_REG_*
+ /// \return The value of the register
+ uint8_t spiReadRegister(uint8_t reg);
+
+ /// Writes a single byte to the NRF24, and at the ame time reads the current STATUS register
+ /// \param[in] reg Register number, one of NRF24_REG_*
+ /// \param[in] val The value to write
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiWriteRegister(uint8_t reg, uint8_t val);
+
+ /// Reads a number of consecutive registers from the NRF24 using burst read mode
+ /// \param[in] reg Register number of the first register, one of NRF24_REG_*
+ /// \param[in] dest Array to write the register values to. Must be at least len bytes
+ /// \param[in] len Number of bytes to read
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len);
+
+ /// Write a number of consecutive registers using burst write mode
+ /// \param[in] reg Register number of the first register, one of NRF24_REG_*
+ /// \param[in] src Array of new register values to write. Must be at least len bytes
+ /// \param[in] len Number of bytes to write
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len);
+
+ /// Reads and returns the device status register NRF24_REG_02_DEVICE_STATUS
+ /// \return The value of the device status register
+ uint8_t statusRead();
+
+ /// Sets the transmit and receive channel number.
+ /// The frequency used is (2400 + channel) MHz
+ /// \return true on success
+ bool setChannel(uint8_t channel);
+
+ /// Sets the chip configuration that will be used to set
+ /// the NRF24 NRF24_REG_00_CONFIG register when in Idle mode. This allows you to change some
+ /// chip configuration for compatibility with libraries other than this one.
+ /// You should not normally need to call this.
+ /// Defaults to NRF24_EN_CRC| RH_NRF24_CRCO, which is the standard configuration for this library
+ /// (2 byte CRC enabled).
+ /// \param[in] mode The chip configuration to be used whe in Idle mode.
+ /// \return true on success
+ bool setOpMode(uint8_t mode);
+
+ /// Sets the Network address.
+ /// Only nodes with the same network address can communicate with each other. You
+ /// can set different network addresses in different sets of nodes to isolate them from each other.
+ /// Internally, this sets the nRF24 TX_ADDR and RX_ADDR_P0 to be the given network address.
+ /// The default network address is 0xE7E7E7E7E7
+ /// \param[in] address The new network address. Must match the network address of any receiving node(s).
+ /// \param[in] len Number of bytes of address to set (3 to 5).
+ /// \return true on success, false if len is not in the range 3-5 inclusive.
+ bool setNetworkAddress(uint8_t* address, uint8_t len);
+
+ /// Sets the data rate and transmitter power to use. Note that the nRF24 and the RFM73 have different
+ /// available power levels, and for convenience, 2 different sets of values are available in the
+ /// RH_NRF24::TransmitPower enum. The ones with the RFM73 only have meaning on the RFM73 and compatible
+ /// devces. The others are for the nRF24.
+ /// \param [in] data_rate The data rate to use for all packets transmitted and received. One of RH_NRF24::DataRate.
+ /// \param [in] power Transmitter power. One of RH_NRF24::TransmitPower.
+ /// \return true on success
+ bool setRF(DataRate data_rate, TransmitPower power);
+
+ /// Sets the radio in power down mode, with the configuration set to the
+ /// last value from setOpMode().
+ /// Sets chip enable to LOW.
+ void setModeIdle();
+
+ /// Sets the radio in RX mode.
+ /// Sets chip enable to HIGH to enable the chip in RX mode.
+ void setModeRx();
+
+ /// Sets the radio in TX mode.
+ /// Pulses the chip enable LOW then HIGH to enable the chip in TX mode.
+ void setModeTx();
+
+ /// Sends data to the address set by setTransmitAddress()
+ /// Sets the radio to TX mode
+ /// \param [in] data Data bytes to send.
+ /// \param [in] len Number of data bytes to send
+ /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was
+ /// successfully transmitted).
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// Blocks until the current message (if any)
+ /// has been transmitted
+ /// \return true on success, false if the chip is not in transmit mode or other transmit failure
+ virtual bool waitPacketSent();
+
+ /// Indicates if the chip is in transmit mode and
+ /// there is a packet currently being transmitted
+ /// \return true if the chip is in transmit mode and there is a transmission in progress
+ bool isSending();
+
+ /// Prints the value of all chip registers
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging purposes only.
+ /// \return true on success
+ bool printRegisters();
+
+ /// Checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+ /// Sets the radio into Power Down mode.
+ /// If successful, the radio will stay in Power Down mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
+ /// \return true if sleep mode was successfully entered.
+ virtual bool sleep();
+
+protected:
+ /// Flush the TX FIFOs
+ /// \return the value of the device status register
+ uint8_t flushTx();
+
+ /// Flush the RX FIFOs
+ /// \return the value of the device status register
+ uint8_t flushRx();
+
+ /// Examine the receive buffer to determine whether the message is for this node
+ void validateRxBuf();
+
+ /// Clear our local receive buffer
+ void clearRxBuf();
+
+private:
+ /// This idle mode chip configuration
+ uint8_t _configuration;
+
+ /// the number of the chip enable pin
+ uint8_t _chipEnablePin;
+
+ /// Number of octets in the buffer
+ uint8_t _bufLen;
+
+ /// The receiver/transmitter buffer
+ uint8_t _buf[RH_NRF24_MAX_PAYLOAD_LEN];
+
+ /// True when there is a valid message in the buffer
+ bool _rxBufValid;
+};
+
+/// @example nrf24_client.pde
+/// @example nrf24_server.pde
+/// @example nrf24_reliable_datagram_client.pde
+/// @example nrf24_reliable_datagram_server.pde
+/// @example RasPiRH.cpp
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF51.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,291 @@
+// NRF51.cpp
+//
+// Per: nRF51_Series_Reference_manual v3.0.pdf
+// Copyright (C) 2012 Mike McCauley
+// $Id: RH_NRF51.cpp,v 1.1 2015/07/01 00:46:05 mikem Exp $
+
+// Set by Arduino IDE when compiling for nRF51 chips:
+#ifdef NRF51
+
+#include <RH_NRF51.h>
+
+RH_NRF51::RH_NRF51()
+ : _rxBufValid(false)
+{
+}
+
+bool RH_NRF51::init()
+{
+ // Enable the High Frequency clock to the system as a whole
+ NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
+ NRF_CLOCK->TASKS_HFCLKSTART = 1;
+ /* Wait for the external oscillator to start up */
+ while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { }
+
+ // Enables the DC/DC converter when the radio is enabled. Need this!
+ NRF_POWER->DCDCEN = 0x00000001;
+
+ // Disable and reset the radio
+ NRF_RADIO->POWER = RADIO_POWER_POWER_Disabled;
+ NRF_RADIO->POWER = RADIO_POWER_POWER_Enabled;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ NRF_RADIO->TASKS_DISABLE = 1;
+ // Wait until we are in DISABLE state
+ while (NRF_RADIO->EVENTS_DISABLED == 0) {}
+
+ // Physical on-air address is set in PREFIX0 + BASE0 by setNetworkAddress
+ NRF_RADIO->TXADDRESS = 0x00; // Use logical address 0 (PREFIX0 + BASE0)
+ NRF_RADIO->RXADDRESSES = 0x01; // Enable reception on logical address 0 (PREFIX0 + BASE0)
+
+ // Configure the CRC
+ NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits
+ NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value
+ NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1
+
+ // These shorts will make the radio transition from Ready to Start to Disable automatically
+ // for both TX and RX, which makes for much shorter on-air times
+ NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos)
+ | (RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos);
+
+ NRF_RADIO->PCNF0 = ((8 << RADIO_PCNF0_LFLEN_Pos) & RADIO_PCNF0_LFLEN_Msk); // Payload length in bits
+
+ // Make sure we are powered down
+ setModeIdle();
+
+ // Set a default network address
+ uint8_t default_network_address[] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
+ setNetworkAddress(default_network_address, sizeof(default_network_address));
+
+ setChannel(2); // The default, in case it was set by another app without powering down
+ setRF(RH_NRF51::DataRate2Mbps, RH_NRF51::TransmitPower0dBm);
+
+ return true;
+}
+
+bool RH_NRF51::setChannel(uint8_t channel)
+{
+ NRF_RADIO->FREQUENCY = ((channel << RADIO_FREQUENCY_FREQUENCY_Pos) & RADIO_FREQUENCY_FREQUENCY_Msk);
+ return true;
+}
+
+bool RH_NRF51::setNetworkAddress(uint8_t* address, uint8_t len)
+{
+ if (len < 3 || len > 5)
+ return false;
+
+ // First byte is the prefix, remainder are base
+ NRF_RADIO->PREFIX0 = ((address[0] << RADIO_PREFIX0_AP0_Pos) & RADIO_PREFIX0_AP0_Msk);
+ uint32_t base;
+ memcpy(&base, address+1, len-1);
+ NRF_RADIO->BASE0 = base;
+
+ NRF_RADIO->PCNF1 = (
+ (((sizeof(_buf)) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk) // maximum length of payload
+ | (((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk) // expand the payload with 0 bytes
+ | (((len-1) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk)); // base address length in number of bytes.
+
+ return true;
+}
+
+bool RH_NRF51::setRF(DataRate data_rate, TransmitPower power)
+{
+ uint8_t mode;
+ uint8_t p;
+
+ if (data_rate == DataRate2Mbps)
+ mode = RADIO_MODE_MODE_Nrf_2Mbit;
+ else if (data_rate == DataRate1Mbps)
+ mode = RADIO_MODE_MODE_Nrf_1Mbit;
+ else if (data_rate == DataRate250kbps)
+ mode = RADIO_MODE_MODE_Nrf_250Kbit;
+ else
+ return false;// Invalid
+
+ if (power == TransmitPower4dBm)
+ p = RADIO_TXPOWER_TXPOWER_Pos4dBm;
+ else if (power == TransmitPower0dBm)
+ p = RADIO_TXPOWER_TXPOWER_0dBm;
+ else if (power == TransmitPowerm4dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg4dBm;
+ else if (power == TransmitPowerm8dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg8dBm;
+ else if (power == TransmitPowerm12dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg12dBm;
+ else if (power == TransmitPowerm16dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg16dBm;
+ else if (power == TransmitPowerm20dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg20dBm;
+ else if (power == TransmitPowerm30dBm)
+ p = RADIO_TXPOWER_TXPOWER_Neg30dBm;
+ else
+ return false; // Invalid
+
+
+ NRF_RADIO->TXPOWER = ((p << RADIO_TXPOWER_TXPOWER_Pos) & RADIO_TXPOWER_TXPOWER_Msk);
+ NRF_RADIO->MODE = ((mode << RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk);
+
+ return true;
+}
+
+void RH_NRF51::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ NRF_RADIO->TASKS_DISABLE = 1;
+ _mode = RHModeIdle;
+ }
+}
+
+void RH_NRF51::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ setModeIdle(); // Can only start RX from DISABLE state
+ // Radio will transition automatically to Disable state when a messageis received
+ NRF_RADIO->PACKETPTR = (uint32_t)_buf;
+ NRF_RADIO->EVENTS_DISABLED = 0U; // So we can detect end of transmission
+ NRF_RADIO->TASKS_RXEN = 1;
+ _mode = RHModeRx;
+ }
+}
+
+void RH_NRF51::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ setModeIdle(); // Can only start RX from DISABLE state
+ // Radio will transition automatically to Disable state at the end of transmission
+ NRF_RADIO->PACKETPTR = (uint32_t)_buf;
+ NRF_RADIO->EVENTS_DISABLED = 0U; // So we can detect end of transmission
+ NRF_RADIO->TASKS_TXEN = 1;
+ _mode = RHModeTx;
+ }
+}
+
+bool RH_NRF51::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_NRF51_MAX_MESSAGE_LEN)
+ return false;
+ // Set up the headers
+ _buf[0] = len + RH_NRF51_HEADER_LEN;
+ _buf[1] = _txHeaderTo;
+ _buf[2] = _txHeaderFrom;
+ _buf[3] = _txHeaderId;
+ _buf[4] = _txHeaderFlags;
+ memcpy(_buf+RH_NRF51_HEADER_LEN+1, data, len);
+
+ _rxBufValid = false;
+ setModeTx();
+ // Radio will return to Disabled state after transmission is complete
+ _txGood++;
+ return true;
+}
+
+bool RH_NRF51::waitPacketSent()
+{
+ // If we are not currently in transmit mode, there is no packet to wait for
+ if (_mode != RHModeTx)
+ return false;
+
+ // When the Disabled event occurs we know the transmission has completed
+ while (NRF_RADIO->EVENTS_DISABLED == 0U)
+ {
+ YIELD;
+ }
+ setModeIdle();
+
+ return true;
+}
+
+bool RH_NRF51::isSending()
+{
+ return (NRF_RADIO->STATE == RADIO_STATE_STATE_Tx) ? true : false;
+}
+
+bool RH_NRF51::printRegisters()
+{
+#ifdef RH_HAVE_SERIAL
+ uint16_t i;
+ uint32_t* p = (uint32_t*)NRF_RADIO;
+ for (i = 0; (p + i) < (uint32_t*) (((NRF_RADIO_Type*)NRF_RADIO) + 1); i++)
+ {
+ Serial.print("Offset: ");
+ Serial.print(i, DEC);
+ Serial.print(" ");
+ Serial.println(*(p+i), HEX);
+ }
+#endif
+
+ return true;
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_NRF51::validateRxBuf()
+{
+ if (_buf[0] < 4)
+ return; // Too short to be a real message
+ // Extract the 4 headers
+ _rxHeaderTo = _buf[1];
+ _rxHeaderFrom = _buf[2];
+ _rxHeaderId = _buf[3];
+ _rxHeaderFlags = _buf[4];
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ _rxGood++;
+ _rxBufValid = true;
+ }
+}
+
+bool RH_NRF51::available()
+{
+ if (!_rxBufValid)
+ {
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx();
+ if (NRF_RADIO->EVENTS_DISABLED == 0U)
+ return false; // No message yet
+ if (NRF_RADIO->CRCSTATUS == ((RADIO_CRCSTATUS_CRCSTATUS_CRCError << RADIO_CRCSTATUS_CRCSTATUS_Pos) & RADIO_CRCSTATUS_CRCSTATUS_Msk))
+ {
+ // Bad CRC, restart the radio
+ _rxBad++;
+ setModeRx();
+ return false;
+ }
+ validateRxBuf();
+ if (_rxBufValid)
+ setModeIdle(); // Got one
+ }
+ return _rxBufValid;
+}
+
+void RH_NRF51::clearRxBuf()
+{
+ _rxBufValid = false;
+ _buf[0] = 0;
+}
+
+bool RH_NRF51::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+ if (buf && len)
+ {
+ // Skip the 4 headers that are at the beginning of the rxBuf
+ // the payload length is the first octet in _buf
+ if (*len > _buf[0]-RH_NRF51_HEADER_LEN)
+ *len = _buf[0]-RH_NRF51_HEADER_LEN;
+ memcpy(buf, _buf+RH_NRF51_HEADER_LEN+1, *len);
+ }
+ clearRxBuf(); // This message accepted and cleared
+ return true;
+}
+
+uint8_t RH_NRF51::maxMessageLength()
+{
+ return RH_NRF51_MAX_MESSAGE_LEN;
+}
+
+#endif // NRF51
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF51.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,242 @@
+// RH_NRF51.h
+// Author: Mike McCauley
+// Copyright (C) 2015 Mike McCauley
+// $Id: RH_NRF51.h,v 1.3 2015/08/14 21:20:12 mikem Exp mikem $
+//
+
+#ifndef RH_NRF51_h
+#define RH_NRF51_h
+
+#include <RHGenericDriver.h>
+
+// This is the maximum number of bytes that can be carried by the nRF51.
+// We use some for headers, keeping fewer for RadioHead messages
+#define RH_NRF51_MAX_PAYLOAD_LEN 254
+
+// The length of the headers we add.
+// The headers are inside the nRF51 payload
+#define RH_NRF51_HEADER_LEN 4
+
+// This is the maximum RadioHead user message length that can be supported by this library. Limited by
+// the supported message lengths in the nRF51
+#define RH_NRF51_MAX_MESSAGE_LEN (RH_NRF51_MAX_PAYLOAD_LEN-RH_NRF51_HEADER_LEN)
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_NRF51 RH_NRF51.h <RH_NRF51.h>
+/// \brief Send and receive addressed datagrams by nRF51 compatible transceivers.
+///
+/// Supported transceivers include:
+/// - Nordic nRF51 based 2.4GHz radio modules, such as nRF51822
+/// and other compatible chips, such as used in RedBearLabs devices like:
+/// http://store.redbearlab.com/products/redbearlab-nrf51822
+/// http://store.redbearlab.com/products/blenano
+///
+/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams
+/// of arbitrary length to 254 octets per packet. Use one of the Manager classes to get addressing and
+/// acknowledgement reliability, routing, meshes etc.
+///
+/// The nRF51822 (https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF51822)
+/// is a complete SoC (system on a chip) with ARM microprocessor and 2.4 GHz radio, which supports a range of channels
+/// and transmission bit rates. Chip antenna is on-board.
+///
+/// This library provides functions for sending and receiving messages of up to 254 octets on any
+/// frequency supported by the nRF51822, at a selected data rate.
+///
+/// The nRF51 transceiver is configured to use Enhanced Shockburst with no acknowledgement and no retransmits.
+/// TXADDRESS and RXADDRESSES:RXADDR0 (ie pipe 0) are the logical address used. The on-air network address
+/// is set in BASE0 and PREFIX0. SHORTS is used to automatically transition the radio between Ready, Start and Disable.
+/// No interrupts are used.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// data rate, and with identical network addresses.
+///
+/// Example programs are included to show the main modes of use.
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this class conform to this packet format. It is NOT compatible
+/// with the one used by RH_NRF24 and the nRF24L01 product specification, mainly because the nRF24 only suports
+/// 6 bits of message length.
+///
+/// - 1 octets PREAMBLE
+/// - 3 to 5 octets NETWORK ADDRESS
+/// - 8 bits PAYLOAD LENGTH
+/// - 0 to 254 octets PAYLOAD, consisting of:
+/// - 1 octet TO header
+/// - 1 octet FROM header
+/// - 1 octet ID header
+/// - 1 octet FLAGS header
+/// - 0 to 250 octets of user message
+/// - 2 octets CRC (Algorithm x^16+x^12^x^5+1 with initial value 0xFFFF).
+///
+/// \par Example programs
+///
+/// Several example programs are provided.
+///
+/// The sample programs are designed to be built using Arduino 1.6.4 or later using the procedures outlined
+/// in http://redbearlab.com/getting-started-nrf51822/
+///
+/// \par Radio Performance
+///
+/// At DataRate2Mbps (2Mb/s), payload length vs airtime:
+/// 0 bytes takes about 70us, 128 bytes takes 520us, 254 bytes take 1020us.
+/// You can extrapolate linearly to slower data rates.
+///
+/// The RF powers claimed by the chip manufacturer have not been independently verified here.
+///
+/// \par Memory
+///
+/// The compiled client and server sketches are about 42k bytes on Arduino.
+/// The reliable client and server sketches compile to about 43k bytes on Arduino. Unfortunately the
+/// Arduino build environmnet does not drop unused clsses and code, so the resulting programs include
+/// all the unused classes ad code. This needs to be revisited.
+/// RAM requirements are minimal.
+///
+class RH_NRF51 : public RHGenericDriver
+{
+public:
+
+ /// \brief Defines convenient values for setting data rates in setRF()
+ typedef enum
+ {
+ DataRate1Mbps = 0, ///< 1 Mbps
+ DataRate2Mbps, ///< 2 Mbps
+ DataRate250kbps ///< 250 kbps
+ } DataRate;
+
+ /// \brief Convenient values for setting transmitter power in setRF()
+ typedef enum
+ {
+ // Add 20dBm for nRF24L01p with PA and LNA modules
+ TransmitPower4dBm = 0, ///< 4 dBm
+ TransmitPower0dBm, ///< 0 dBm
+ TransmitPowerm4dBm, ///< -4 dBm
+ TransmitPowerm8dBm, ///< -8 dBm
+ TransmitPowerm12dBm, ///< -12 dBm
+ TransmitPowerm16dBm, ///< -16 dBm
+ TransmitPowerm20dBm, ///< -20 dBm
+ TransmitPowerm30dBm, ///< -30 dBm
+ } TransmitPower;
+
+ /// Constructor.
+ /// After constructing, you must call init() to initialise the interface
+ /// and the radio module
+ RH_NRF51();
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:
+ /// - Start the processors High Frequency clock DC/DC converter and
+ /// - Disable and reset the radio
+ /// - Set the logical channel to 0 for transmit and receive (only pipe 0 is used)
+ /// - Configure the CRC (2 octets, algorithm x^16+x^12^x^5+1 with initial value 0xffff)
+ /// - Set the default network address of 0xE7E7E7E7E7
+ /// - Set channel to 2
+ /// - Set data rate to DataRate2Mbps
+ /// - Set TX power to TransmitPower0dBm
+ /// \return true if everything was successful
+ bool init();
+
+ /// Sets the transmit and receive channel number.
+ /// The frequency used is (2400 + channel) MHz
+ /// \return true on success
+ bool setChannel(uint8_t channel);
+
+ /// Sets the Network address.
+ /// Only nodes with the same network address can communicate with each other. You
+ /// can set different network addresses in different sets of nodes to isolate them from each other.
+ /// Internally, this sets the nRF51 BASE0 and PREFIX0 to be the given network address.
+ /// The first octet of the address is used for PREFIX0 and the rest is used for BASE0. BALEN is
+ /// set to the approprtae base length.
+ /// The default network address is 0xE7E7E7E7E7.
+ /// \param[in] address The new network address. Must match the network address of any receiving node(s).
+ /// \param[in] len Number of bytes of address to set (3 to 5).
+ /// \return true on success, false if len is not in the range 3-5 inclusive.
+ bool setNetworkAddress(uint8_t* address, uint8_t len);
+
+ /// Sets the data rate and transmitter power to use.
+ /// \param [in] data_rate The data rate to use for all packets transmitted and received. One of RH_NRF51::DataRate.
+ /// \param [in] power Transmitter power. One of RH_NRF51::TransmitPower.
+ /// \return true on success
+ bool setRF(DataRate data_rate, TransmitPower power);
+
+ /// Sets the radio in power down mode, with the configuration set to the
+ /// last value from setOpMode().
+ /// Sets chip enable to LOW.
+ void setModeIdle();
+
+ /// Sets the radio in RX mode.
+ void setModeRx();
+
+ /// Sets the radio in TX mode.
+ void setModeTx();
+
+ /// Sends data to the address set by setTransmitAddress()
+ /// Sets the radio to TX mode.
+ /// \param [in] data Data bytes to send.
+ /// \param [in] len Number of data bytes to send
+ /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was
+ /// successfully transmitted).
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// Blocks until the current message (if any)
+ /// has been transmitted
+ /// \return true on success, false if the chip is not in transmit mode or other transmit failure
+ virtual bool waitPacketSent();
+
+ /// Indicates if the chip is in transmit mode and
+ /// there is a packet currently being transmitted
+ /// \return true if the chip is in transmit mode and there is a transmission in progress
+ bool isSending();
+
+ /// Prints the value of all NRF_RADIO registers.
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging purposes only.
+ /// Caution: there are 1024 of them (many reserved and set to 0).
+ /// \return true on success
+ bool printRegisters();
+
+ /// Checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// Once a message with CRC correct is received, the receiver will be returned to Idle mode.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+protected:
+ /// Examine the receive buffer to determine whether the message is for this node
+ void validateRxBuf();
+
+ /// Clear our local receive buffer
+ void clearRxBuf();
+
+private:
+ /// The receiver/transmitter buffer
+ /// First octet is the payload length, remainder is the payload
+ uint8_t _buf[RH_NRF51_MAX_PAYLOAD_LEN+1];
+
+ /// True when there is a valid message in the buffer
+ bool _rxBufValid;
+};
+
+/// @example nrf51_client.pde
+/// @example nrf51_server.pde
+/// @example nrf51_reliable_datagram_client.pde
+/// @example nrf51_reliable_datagram_server.pde
+/// @example nrf51_audio_tx.pde
+/// @example nrf51_audio_rx.pde
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF905.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,264 @@
+// RH_NRF905.cpp
+//
+// Copyright (C) 2012 Mike McCauley
+// $Id: RH_NRF905.cpp,v 1.5 2015/08/12 23:18:51 mikem Exp $
+
+#include <RH_NRF905.h>
+
+RH_NRF905::RH_NRF905(PINS chipEnablePin, PINS txEnablePin, PINS slaveSelectPin, RHGenericSPI& spi)
+ :
+ RHNRFSPIDriver(slaveSelectPin, spi)
+{
+ _chipEnablePin = chipEnablePin;
+ _txEnablePin = txEnablePin;
+}
+
+bool RH_NRF905::init()
+{
+#if defined (__MK20DX128__) || defined (__MK20DX256__)
+ // Teensy is unreliable at 8MHz:
+ _spi.setFrequency(RHGenericSPI::Frequency1MHz);
+#else
+ _spi.setFrequency(RHGenericSPI::Frequency8MHz);
+#endif
+ if (!RHNRFSPIDriver::init())
+ return false;
+
+ // Initialise the slave select pin and the tx Enable pin
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_chipEnablePin, OUTPUT);
+ pinMode(_txEnablePin, OUTPUT);
+#endif
+ digitalWrite(_chipEnablePin, LOW);
+ digitalWrite(_txEnablePin, LOW);
+
+ // Configure the chip
+ // CRC 16 bits enabled. 16MHz crystal freq
+ spiWriteRegister(RH_NRF905_CONFIG_9, RH_NRF905_CONFIG_9_CRC_EN | RH_NRF905_CONFIG_9_CRC_MODE_16BIT | RH_NRF905_CONFIG_9_XOF_16MHZ);
+
+ // Make sure we are powered down
+ setModeIdle();
+
+ // Some innocuous defaults
+ setChannel(108, LOW); // 433.2 MHz
+ setRF(RH_NRF905::TransmitPowerm10dBm);
+
+ return true;
+}
+
+// Use the register commands to read and write the registers
+uint8_t RH_NRF905::spiReadRegister(uint8_t reg)
+{
+ return spiRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG);
+}
+
+uint8_t RH_NRF905::spiWriteRegister(uint8_t reg, uint8_t val)
+{
+ return spiWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, val);
+}
+
+uint8_t RH_NRF905::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len)
+{
+ return spiBurstRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG, dest, len);
+}
+
+uint8_t RH_NRF905::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len)
+{
+ return spiBurstWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, src, len);
+}
+
+uint8_t RH_NRF905::statusRead()
+{
+ // The status is a byproduct of sending a command
+ return spiCommand(0);
+}
+
+bool RH_NRF905::setChannel(uint16_t channel, bool hiFrequency)
+{
+ spiWriteRegister(RH_NRF905_CONFIG_0, channel & RH_NRF905_CONFIG_0_CH_NO);
+ // Set or clear the high bit of the channel
+ uint8_t bit8 = (channel >> 8) & 0x01;
+ uint8_t reg1 = spiReadRegister(RH_NRF905_CONFIG_1);
+ reg1 = (reg1 & ~0x01) | bit8;
+ // Set or clear the HFREQ_PLL bit
+ reg1 &= ~RH_NRF905_CONFIG_1_HFREQ_PLL;
+ if (hiFrequency)
+ reg1 |= RH_NRF905_CONFIG_1_HFREQ_PLL;
+ spiWriteRegister(RH_NRF905_CONFIG_1, reg1);
+ return true;
+}
+
+bool RH_NRF905::setNetworkAddress(uint8_t* address, uint8_t len)
+{
+ if (len < 1 || len > 4)
+ return false;
+ // Set RX_AFW and TX_AFW
+ spiWriteRegister(RH_NRF905_CONFIG_2, len | (len << 4));
+ spiBurstWrite(RH_NRF905_REG_W_TX_ADDRESS, address, len);
+ spiBurstWriteRegister(RH_NRF905_CONFIG_5, address, len);
+ return true;
+}
+
+bool RH_NRF905::setRF(TransmitPower power)
+{
+ // Enum definitions of power are the same numerical values as the register
+ spiWriteRegister(RH_NRF905_CONFIG_1_PA_PWR, power);
+ return true;
+}
+
+void RH_NRF905::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ digitalWrite(_chipEnablePin, LOW);
+ digitalWrite(_txEnablePin, LOW);
+ _mode = RHModeIdle;
+ }
+}
+
+void RH_NRF905::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ digitalWrite(_txEnablePin, LOW);
+ digitalWrite(_chipEnablePin, HIGH);
+ _mode = RHModeRx;
+ }
+}
+
+void RH_NRF905::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ // Its the high transition that puts us into TX mode
+ digitalWrite(_txEnablePin, HIGH);
+ digitalWrite(_chipEnablePin, HIGH);
+ _mode = RHModeTx;
+ }
+}
+
+bool RH_NRF905::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_NRF905_MAX_MESSAGE_LEN)
+ return false;
+ // Set up the headers
+ _buf[0] = _txHeaderTo;
+ _buf[1] = _txHeaderFrom;
+ _buf[2] = _txHeaderId;
+ _buf[3] = _txHeaderFlags;
+ _buf[4] = len;
+ memcpy(_buf+RH_NRF905_HEADER_LEN, data, len);
+ spiBurstWrite(RH_NRF905_REG_W_TX_PAYLOAD, _buf, len + RH_NRF905_HEADER_LEN);
+ setModeTx();
+ // Radio will return to Standby mode after transmission is complete
+ _txGood++;
+ return true;
+}
+
+bool RH_NRF905::waitPacketSent()
+{
+ if (_mode != RHModeTx)
+ return false;
+
+ while (!(statusRead() & RH_NRF905_STATUS_DR))
+ YIELD;
+ setModeIdle();
+ return true;
+}
+
+bool RH_NRF905::isSending()
+{
+ if (_mode != RHModeTx)
+ return false;
+
+ return !(statusRead() & RH_NRF905_STATUS_DR);
+}
+
+bool RH_NRF905::printRegister(uint8_t reg)
+{
+#ifdef RH_HAVE_SERIAL
+ Serial.print(reg, HEX);
+ Serial.print(": ");
+ Serial.println(spiReadRegister(reg), HEX);
+#endif
+
+ return true;
+}
+
+bool RH_NRF905::printRegisters()
+{
+ uint8_t registers[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+
+ uint8_t i;
+ for (i = 0; i < sizeof(registers); i++)
+ printRegister(registers[i]);
+ return true;
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_NRF905::validateRxBuf()
+{
+ // Check the length
+ uint8_t len = _buf[4];
+ if (len > RH_NRF905_MAX_MESSAGE_LEN)
+ return; // Silly LEN header
+
+ // Extract the 4 headers
+ _rxHeaderTo = _buf[0];
+ _rxHeaderFrom = _buf[1];
+ _rxHeaderId = _buf[2];
+ _rxHeaderFlags = _buf[3];
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ _rxGood++;
+ _bufLen = len + RH_NRF905_HEADER_LEN; // _buf still includes the headers
+ _rxBufValid = true;
+ }
+}
+
+bool RH_NRF905::available()
+{
+ if (!_rxBufValid)
+ {
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx();
+ if (!(statusRead() & RH_NRF905_STATUS_DR))
+ return false;
+ // Get the message into the RX buffer, so we can inspect the headers
+ // we still dont know how long is the user message
+ spiBurstRead(RH_NRF905_REG_R_RX_PAYLOAD, _buf, RH_NRF905_MAX_PAYLOAD_LEN);
+ validateRxBuf();
+ if (_rxBufValid)
+ setModeIdle(); // Got one
+ }
+ return _rxBufValid;
+}
+
+void RH_NRF905::clearRxBuf()
+{
+ _rxBufValid = false;
+ _bufLen = 0;
+}
+
+bool RH_NRF905::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+ if (buf && len)
+ {
+ // Skip the 4 headers that are at the beginning of the rxBuf
+ if (*len > _bufLen-RH_NRF905_HEADER_LEN)
+ *len = _bufLen-RH_NRF905_HEADER_LEN;
+ memcpy(buf, _buf+RH_NRF905_HEADER_LEN, *len);
+ }
+ clearRxBuf(); // This message accepted and cleared
+ return true;
+}
+
+uint8_t RH_NRF905::maxMessageLength()
+{
+ return RH_NRF905_MAX_MESSAGE_LEN;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_NRF905.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,396 @@
+// RH_NRF905.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_NRF905.h,v 1.7 2015/03/09 06:04:26 mikem Exp $
+//
+
+#ifndef RH_NRF905_h
+#define RH_NRF905_h
+
+#include <RHGenericSPI.h>
+#include <RHNRFSPIDriver.h>
+
+// This is the maximum (and only) number of bytes that can be carried by the nRF905.
+// We use some for headers, leaving fewer for RadioHead messages
+#define RH_NRF905_MAX_PAYLOAD_LEN 32
+
+// The length of the headers we add.
+// The headers are inside the nRF905 payload
+// As well as the usual TO, FROM, ID, FLAGS, we also need LEN, since
+// nRF905 only has fixed width messages.
+// REVISIT: could we have put the LEN into the FLAGS field?
+#define RH_NRF905_HEADER_LEN 5
+
+// This is the maximum RadioHead user message length that can be supported by this library. Limited by
+// the supported message lengths in the nRF905
+#define RH_NRF905_MAX_MESSAGE_LEN (RH_NRF905_MAX_PAYLOAD_LEN-RH_NRF905_HEADER_LEN)
+
+// Register names
+#define RH_NRF905_REG_MASK 0x0f
+#define RH_NRF905_REG_W_CONFIG 0x00
+#define RH_NRF905_REG_R_CONFIG 0x10
+#define RH_NRF905_REG_W_TX_PAYLOAD 0x20
+#define RH_NRF905_REG_R_TX_PAYLOAD 0x21
+#define RH_NRF905_REG_W_TX_ADDRESS 0x22
+#define RH_NRF905_REG_R_TX_ADDRESS 0x23
+#define RH_NRF905_REG_R_RX_PAYLOAD 0x24
+#define RH_NRF905_REG_CHANNEL_CONFIG 0x80
+
+// Configuration register
+#define RH_NRF905_CONFIG_0 0x00
+#define RH_NRF905_CONFIG_0_CH_NO 0xff
+
+#define RH_NRF905_CONFIG_1 0x01
+#define RH_NRF905_CONFIG_1_AUTO_RETRAN 0x20
+#define RH_NRF905_CONFIG_1_RX_RED_PWR 0x10
+#define RH_NRF905_CONFIG_1_PA_PWR 0x0c
+#define RH_NRF905_CONFIG_1_PA_PWR_N10DBM 0x00
+#define RH_NRF905_CONFIG_1_PA_PWR_N2DBM 0x04
+#define RH_NRF905_CONFIG_1_PA_PWR_6DBM 0x08
+#define RH_NRF905_CONFIG_1_PA_PWR_10DBM 0x0c
+#define RH_NRF905_CONFIG_1_HFREQ_PLL 0x02
+#define RH_NRF905_CONFIG_1_CH_NO 0x01
+
+#define RH_NRF905_CONFIG_2 0x02
+#define RH_NRF905_CONFIG_2_TX_AFW 0x70
+#define RH_NRF905_CONFIG_2_RX_AFW 0x07
+
+#define RH_NRF905_CONFIG_3 0x03
+#define RH_NRF905_CONFIG_3_RX_PW 0x3f
+
+#define RH_NRF905_CONFIG_4 0x04
+#define RH_NRF905_CONFIG_4_TX_PW 0x3f
+
+#define RH_NRF905_CONFIG_5 0x05
+#define RH_NRF905_CONFIG_5_RX_ADDRESS 0xff
+
+#define RH_NRF905_CONFIG_6 0x06
+#define RH_NRF905_CONFIG_6_RX_ADDRESS 0xff
+
+#define RH_NRF905_CONFIG_7 0x07
+#define RH_NRF905_CONFIG_7_RX_ADDRESS 0xff
+
+#define RH_NRF905_CONFIG_8 0x08
+#define RH_NRF905_CONFIG_8_RX_ADDRESS 0xff
+
+#define RH_NRF905_CONFIG_9 0x09
+#define RH_NRF905_CONFIG_9_CRC_MODE_16BIT 0x80
+#define RH_NRF905_CONFIG_9_CRC_EN 0x40
+#define RH_NRF905_CONFIG_9_XOF 0x38
+#define RH_NRF905_CONFIG_9_XOF_4MHZ 0x00
+#define RH_NRF905_CONFIG_9_XOF_8MHZ 0x08
+#define RH_NRF905_CONFIG_9_XOF_12MHZ 0x10
+#define RH_NRF905_CONFIG_9_XOF_16MHZ 0x18
+#define RH_NRF905_CONFIG_9_XOF_20MHZ 0x20
+#define RH_NRF905_CONFIG_9_UP_CLK_EN 0x04
+#define RH_NRF905_CONFIG_9_UP_CLK_FREQ 0x03
+#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_4MHZ 0x00
+#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_2MHZ 0x01
+#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_1MHZ 0x02
+#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_500KHZ 0x03
+
+// Status register is always read as first byte
+#define RH_NRF905_STATUS_AM 0x80
+#define RH_NRF905_STATUS_DR 0x20
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_NRF905 RH_NRF905.h <RH_NRF905.h>
+/// \brief Send and receive addressed, reliable, acknowledged datagrams by nRF905 and compatible transceivers.
+///
+/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams
+/// of arbitrary length to 28 octets per packet. Use one of the Manager classes to get addressing and
+/// acknowledgement reliability, routing, meshes etc.
+///
+/// The nRF905 transceiver is configured to use Enhanced Shockburst with 16 Bit CRC, and 32 octet packets.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency
+/// and with identical network addresses.
+///
+/// The nRF905 from Nordic Semiconductor http://www.nordicsemi.com/eng/Products/Sub-1-GHz-RF/nRF905
+/// (http://www.nordicsemi.com/jpn/nordic/content_download/2452/29528/file/Product_Specification_nRF905_v1.5.pdf)
+/// is a low-cost 433/868/915 MHz ISM transceiver module. It supports a number of channel frequencies at
+/// 100kHz deviation and 50kHz bandwidth with Manchester encoding.
+///
+/// We tested with inexpensive nRF905 modules from eBay, similar to:
+/// http://www.aliexpress.com/store/product/Free-ship-NRF905-433MHz-Wireless-Transmission-Module-Transceiver-Module-with-Antenna-for-the-433MHz-ISM-band/513046_607163305.html
+///
+/// This library provides functions for sending and receiving messages of up to 27 octets on any
+/// frequency supported by the nRF905.
+///
+/// Several nRF905 modules can be connected to an Arduino, permitting the construction of translators
+/// and frequency changers, etc.
+///
+/// Example Arduino programs are included to show the main modes of use.
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this class conform to this fixed length packet format
+///
+/// - 4 octets NETWORK ADDRESS
+/// - 32 octets PAYLOAD, consisting of:
+/// - 1 octet TO header
+/// - 1 octet FROM header
+/// - 1 octet ID header
+/// - 1 octet FLAGS header
+/// - 1 octet user message length header
+/// - 0 to 27 octets of user message, trailing octets after the user message length are ignored
+/// - 2 octets CRC
+///
+/// All messages sent and received by this driver are 32 octets. The user message length is embedded in the message.
+///
+/// \par Connecting nRF905
+///
+/// The nRF905 is a 3.3V part is is *NOT* 5V tolerant. So you MUST use a 3.3V CPU such as Teensy, Arduino Due etc
+/// or else provide for level shifters between the CPU and the nRF905. Failure to consider this will probbaly
+/// break your nRF905.
+///
+/// The electrical connection between the nRF905 and the CPU require 3.3V, the 3 x SPI pins (SCK, SDI, SDO),
+/// a Chip Enable pin, a Transmit Enable pin and a Slave Select pin.
+///
+/// The examples below assume the commonly found cheap Chinese nRF905 modules. The RH_RF905 driver assumes the
+/// the nRF905 has a 16MHz crystal.
+///
+/// Connect the nRF905 to Teensy like this
+/// \code
+/// CPU nRF905 module
+/// 3V3----------VCC (3.3V)
+/// pin D8-----------CE (chip enable in)
+/// pin D9-----------TX_EN (transmit enable in)
+/// SS pin D10----------CSN (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------MOSI (SPI Data in)
+/// MISO pin D12----------MISO (SPI data out)
+/// GND----------GND (ground in)
+/// \endcode
+///
+/// Caution: Arduino Due is a 3.3V part and is not 5V tolerant (so too is the nRF905 module
+/// so they can be connected directly together. Unlike other Arduinos the Due has it default SPI
+/// connections on a dedicated 6 pin SPI header in the center of the board, which is
+/// physically compatible with Uno, Leonardo and Mega2560. A little dot marks pin 1 on the header.
+/// You must connect to these
+/// and *not* to the usual Arduino SPI pins 11, 12 and 13.
+/// See http://21stdigitalhome.blogspot.com.au/2013/02/arduino-due-hardware-spi.html
+///
+/// Connect the nRF905 to Arduino Due like this
+/// \code
+/// CPU nRF905 module
+/// 3V3----------VCC (3.3V)
+/// pin D8-----------CE (chip enable in)
+/// pin D9-----------TX_EN (transmit enable in)
+/// SS pin D10----------CSN (chip select in)
+/// SCK on SPI header pin 3----------SCK (SPI clock in)
+/// MOSI on SPI header pin 4----------MOSI (SPI Data in)
+/// MISO on SPI header pin 1----------MISO (SPI data out)
+/// GND----------GND (ground in)
+/// \endcode
+///
+/// and you can then use the default constructor RH_NRF905().
+/// You can override the default settings for the CE, TX_EN and CSN pins
+/// in the NRF905() constructor if you wish to connect the slave select CSN to other than the normal one for your
+/// CPU.
+///
+/// It is possible to have 2 radios conected to one CPU, provided each radio has its own
+/// CSN, TX_EN and CE line (SCK, MOSI and MISO are common to both radios)
+///
+/// \par Example programs
+///
+/// Several example programs are provided. They work out of the box with Teensy 3.1 and Arduino Due
+/// connected as show above.
+///
+/// \par Radio Performance
+///
+/// Frequency accuracy may be debatable.
+///
+/// \par Memory
+///
+/// Memory usage of this class is minimal. The compiled client and server sketches are about 16000 bytes on Teensy.
+///
+class RH_NRF905 : public RHNRFSPIDriver
+{
+public:
+ /// \brief Convenient values for setting transmitter power in setRF()
+ /// These are designed to agree with the values for PA_PWR
+ /// To be passed to setRF();
+ typedef enum
+ {
+ TransmitPowerm10dBm = 0, ///< -10 dBm
+ TransmitPowerm2dBm, ///< -2 dBm
+ TransmitPower6dBm, ///< 6 dBm
+ TransmitPower10dBm ///< 10 dBm
+ } TransmitPower;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// chip enable and slave select pin.
+ /// After constructing, you must call init() to initialise the interface
+ /// and the radio module
+ /// \param[in] chipEnablePin the Arduino pin to use to enable the chip for transmit/receive
+ /// \param[in] txEnablePin the Arduino pin cponnected to the txEn pin on the radio that enable transmit mode
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the NRF905 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega,
+ /// D10 for Maple, Teensy)
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_NRF905(PINS chipEnablePin, PINS txEnablePin, PINS slaveSelectPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:g
+ /// - Set the chip enable and chip select pins to output LOW, HIGH respectively.
+ /// - Initialise the SPI output pins
+ /// - Initialise the SPI interface library to 8MHz (Hint, if you want to lower
+ /// the SPI frequency (perhaps where you have other SPI shields, low voltages etc),
+ /// call SPI.setClockDivider() after init()).
+ /// -Flush the receiver and transmitter buffers
+ /// - Set the radio to receive with powerUpRx();
+ /// \return true if everything was successful
+ bool init();
+
+ /// Reads a single register from the NRF905
+ /// \param[in] reg Register number, one of NR905_REG_*
+ /// \return The value of the register
+ uint8_t spiReadRegister(uint8_t reg);
+
+ /// Writes a single byte to the NRF905, and at the ame time reads the current STATUS register
+ /// \param[in] reg Register number, one of NRF905_REG_*
+ /// \param[in] val The value to write
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiWriteRegister(uint8_t reg, uint8_t val);
+
+ /// Reads a number of consecutive registers from the NRF905 using burst read mode
+ /// \param[in] reg Register number of the first register, one of NRF905_REG_*
+ /// \param[in] dest Array to write the register values to. Must be at least len bytes
+ /// \param[in] len Number of bytes to read
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len);
+
+ /// Write a number of consecutive registers using burst write mode
+ /// \param[in] reg Register number of the first register, one of NRF905_REG_*
+ /// \param[in] src Array of new register values to write. Must be at least len bytes
+ /// \param[in] len Number of bytes to write
+ /// \return the current STATUS (read while the command is sent)
+ uint8_t spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len);
+
+ /// Reads and returns the device status register NRF905_REG_02_DEVICE_STATUS
+ /// \return The value of the device status register
+ uint8_t statusRead();
+
+ /// Sets the transmit and receive channel number.
+ /// The RF frequency used is (422.4 + channel/10) * (1+hiFrequency) MHz
+ /// \param[in] channel The channel number.
+ /// \param[in] hiFrequency false for low frequency band (422.4MHz and up), true for high frequency band (845MHz and up)
+ /// \return true on success
+ bool setChannel(uint16_t channel, bool hiFrequency = false);
+
+ /// Sets the Network address.
+ /// Only nodes with the same network address can communicate with each other. You
+ /// can set different network addresses in different sets of nodes to isolate them from each other.
+ /// The default network address is 0xE7E7E7E7
+ /// \param[in] address The new network address. Must match the network address of any receiving node(s).
+ /// \param[in] len Number of bytes of address to set (1 to 4).
+ /// \return true on success, false if len is not in the range 1-4 inclusive.
+ bool setNetworkAddress(uint8_t* address, uint8_t len);
+
+ /// Sets the transmitter power to use
+ /// \param [in] power Transmitter power. One of NRF905::TransmitPower.
+ /// \return true on success
+ bool setRF(TransmitPower power);
+
+ /// Sets the radio in power down mode.
+ /// Sets chip enable to LOW.
+ /// \return true on success
+ void setModeIdle();
+
+ /// Sets the radio in RX mode.
+ /// Sets chip enable to HIGH to enable the chip in RX mode.
+ /// \return true on success
+ void setModeRx();
+
+ /// Sets the radio in TX mode.
+ /// Pulses the chip enable LOW then HIGH to enable the chip in TX mode.
+ /// \return true on success
+ void setModeTx();
+
+ /// Sends data to the address set by setTransmitAddress()
+ /// Sets the radio to TX mode
+ /// \param [in] data Data bytes to send.
+ /// \param [in] len Number of data bytes to set in teh TX buffer. The actual size of the
+ /// transmitted data payload is set by setPayloadSize
+ /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was
+ /// successfully transmitted).
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// Blocks until the current message (if any)
+ /// has been transmitted
+ /// \return true on success, false if the chip is not in transmit mode
+ virtual bool waitPacketSent();
+
+ /// Indicates if the chip is in transmit mode and
+ /// there is a packet currently being transmitted
+ /// \return true if the chip is in transmit mode and there is a transmission in progress
+ bool isSending();
+
+ /// Prints the value of a single chip register
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging purposes only.
+ /// \return true on success
+ bool printRegister(uint8_t reg);
+
+ /// Prints the value of all chip registers
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging purposes only.
+ /// \return true on success
+ bool printRegisters();
+
+ /// Checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+protected:
+ /// Examine the revceive buffer to determine whether the message is for this node
+ void validateRxBuf();
+
+ /// Clear our local receive buffer
+ void clearRxBuf();
+
+private:
+ /// This idle mode chip configuration
+ uint8_t _configuration;
+
+ /// the number of the chip enable pin
+ uint8_t _chipEnablePin;
+
+ /// The number of the transmit enable pin
+ uint8_t _txEnablePin;
+
+ /// Number of octets in the buffer
+ uint8_t _bufLen;
+
+ /// The receiver/transmitter buffer
+ uint8_t _buf[RH_NRF905_MAX_PAYLOAD_LEN];
+
+ /// True when there is a valid message in the buffer
+ bool _rxBufValid;
+};
+
+/// @example nrf905_client.pde
+/// @example nrf905_server.pde
+/// @example nrf905_reliable_datagram_client.pde
+/// @example nrf905_reliable_datagram_server.pde
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF22.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,748 @@
+// RH_RF22.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF22.cpp,v 1.24 2015/05/17 00:11:26 mikem Exp $
+
+#include <RH_RF22.h>
+
+// Interrupt vectors for the 2 Arduino interrupt pins
+// Each interrupt can be handled by a different instance of RH_RF22, allowing you to have
+// 2 RH_RF22s per Arduino
+RH_RF22* RH_RF22::_deviceForInterrupt[RH_RF22_NUM_INTERRUPTS] = {0, 0, 0};
+uint8_t RH_RF22::_interruptCount = 0; // Index into _deviceForInterrupt for next device
+
+// These are indexed by the values of ModemConfigChoice
+// Canned modem configurations generated with
+// http://www.hoperf.com/upload/rf/RH_RF22B%2023B%2031B%2042B%2043B%20Register%20Settings_RevB1-v5.xls
+// Stored in flash (program) memory to save SRAM
+PROGMEM static const RH_RF22::ModemConfig MODEM_CONFIG_TABLE[] =
+{
+ { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x00, 0x08 }, // Unmodulated carrier
+ { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x33, 0x08 }, // FSK, PN9 random modulation, 2, 5
+
+ // All the following enable FIFO with reg 71
+ // 1c, 1f, 20, 21, 22, 23, 24, 25, 2c, 2d, 2e, 58, 69, 6e, 6f, 70, 71, 72
+ // FSK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm
+ { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x22, 0x08 }, // 2, 5
+ { 0x1b, 0x03, 0x41, 0x60, 0x27, 0x52, 0x00, 0x07, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x22, 0x3a }, // 2.4, 36
+ { 0x1d, 0x03, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x13, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x22, 0x48 }, // 4.8, 45
+ { 0x1e, 0x03, 0xd0, 0x00, 0x9d, 0x49, 0x00, 0x45, 0x40, 0x0a, 0x20, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x22, 0x48 }, // 9.6, 45
+ { 0x2b, 0x03, 0x34, 0x02, 0x75, 0x25, 0x07, 0xff, 0x40, 0x0a, 0x1b, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x22, 0x0f }, // 19.2, 9.6
+ { 0x02, 0x03, 0x68, 0x01, 0x3a, 0x93, 0x04, 0xd5, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x22, 0x1f }, // 38.4, 19.6
+ { 0x06, 0x03, 0x45, 0x01, 0xd7, 0xdc, 0x07, 0x6e, 0x40, 0x0a, 0x2d, 0x80, 0x60, 0x0e, 0xbf, 0x0c, 0x22, 0x2e }, // 57.6. 28.8
+ { 0x8a, 0x03, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x40, 0x0a, 0x50, 0x80, 0x60, 0x20, 0x00, 0x0c, 0x22, 0xc8 }, // 125, 125
+
+ { 0x2b, 0x03, 0xa1, 0xe0, 0x10, 0xc7, 0x00, 0x09, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x04, 0x32, 0x2c, 0x22, 0x04 }, // 512 baud, FSK, 2.5 Khz fd for POCSAG compatibility
+ { 0x27, 0x03, 0xa1, 0xe0, 0x10, 0xc7, 0x00, 0x06, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x04, 0x32, 0x2c, 0x22, 0x07 }, // 512 baud, FSK, 4.5 Khz fd for POCSAG compatibility
+
+ // GFSK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm
+ // These differ from FSK only in register 71, for the modulation type
+ { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x23, 0x08 }, // 2, 5
+ { 0x1b, 0x03, 0x41, 0x60, 0x27, 0x52, 0x00, 0x07, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x23, 0x3a }, // 2.4, 36
+ { 0x1d, 0x03, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x13, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x23, 0x48 }, // 4.8, 45
+ { 0x1e, 0x03, 0xd0, 0x00, 0x9d, 0x49, 0x00, 0x45, 0x40, 0x0a, 0x20, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x23, 0x48 }, // 9.6, 45
+ { 0x2b, 0x03, 0x34, 0x02, 0x75, 0x25, 0x07, 0xff, 0x40, 0x0a, 0x1b, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x23, 0x0f }, // 19.2, 9.6
+ { 0x02, 0x03, 0x68, 0x01, 0x3a, 0x93, 0x04, 0xd5, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x23, 0x1f }, // 38.4, 19.6
+ { 0x06, 0x03, 0x45, 0x01, 0xd7, 0xdc, 0x07, 0x6e, 0x40, 0x0a, 0x2d, 0x80, 0x60, 0x0e, 0xbf, 0x0c, 0x23, 0x2e }, // 57.6. 28.8
+ { 0x8a, 0x03, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x40, 0x0a, 0x50, 0x80, 0x60, 0x20, 0x00, 0x0c, 0x23, 0xc8 }, // 125, 125
+
+ // OOK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm
+ { 0x51, 0x03, 0x68, 0x00, 0x3a, 0x93, 0x01, 0x3d, 0x2c, 0x11, 0x28, 0x80, 0x60, 0x09, 0xd5, 0x2c, 0x21, 0x08 }, // 1.2, 75
+ { 0xc8, 0x03, 0x39, 0x20, 0x68, 0xdc, 0x00, 0x6b, 0x2a, 0x08, 0x2a, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x21, 0x08 }, // 2.4, 335
+ { 0xc8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x29, 0x04, 0x29, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x21, 0x08 }, // 4.8, 335
+ { 0xb8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x82, 0x29, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x21, 0x08 }, // 9.6, 335
+ { 0xa8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x41, 0x29, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x21, 0x08 }, // 19.2, 335
+ { 0x98, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x20, 0x29, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x21, 0x08 }, // 38.4, 335
+ { 0x98, 0x03, 0x96, 0x00, 0xda, 0x74, 0x00, 0xdc, 0x28, 0x1f, 0x29, 0x80, 0x60, 0x0a, 0x3d, 0x0c, 0x21, 0x08 }, // 40, 335
+};
+
+RH_RF22::RH_RF22(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi)
+ :
+ RHSPIDriver(slaveSelectPin, spi),
+ _interruptPin(interruptPin)
+{
+ _idleMode = RH_RF22_XTON; // Default idle state is READY mode
+ _polynomial = CRC_16_IBM; // Historical
+ _myInterruptIndex = 0xff; // Not allocated yet
+}
+
+void RH_RF22::setIdleMode(uint8_t idleMode)
+{
+ _idleMode = idleMode;
+}
+
+bool RH_RF22::init()
+{
+ if (!RHSPIDriver::init())
+ return false;
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Determine the interrupt number that corresponds to the interruptPin
+ int interruptNumber = digitalPinToInterrupt(_interruptPin);
+ if (interruptNumber == NOT_AN_INTERRUPT)
+ return false;
+#endif
+
+ // Software reset the device
+ reset();
+
+ // Get the device type and check it
+ // This also tests whether we are really connected to a device
+ _deviceType = spiRead(RH_RF22_REG_00_DEVICE_TYPE);
+ if ( _deviceType != RH_RF22_DEVICE_TYPE_RX_TRX
+ && _deviceType != RH_RF22_DEVICE_TYPE_TX)
+ {
+ return false;
+ }
+
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
+ // ARM M4 requires the below. else pin interrupt doesn't work properly.
+ // On all other platforms, its innocuous, belt and braces
+ pinMode(_interruptPin, INPUT);
+#endif
+
+ // Enable interrupt output on the radio. Interrupt line will now go high until
+ // an interrupt occurs
+ spiWrite(RH_RF22_REG_05_INTERRUPT_ENABLE1, RH_RF22_ENTXFFAEM | RH_RF22_ENRXFFAFULL | RH_RF22_ENPKSENT | RH_RF22_ENPKVALID | RH_RF22_ENCRCERROR | RH_RF22_ENFFERR);
+ spiWrite(RH_RF22_REG_06_INTERRUPT_ENABLE2, RH_RF22_ENPREAVAL);
+
+ // Set up interrupt handler
+ // Since there are a limited number of interrupt glue functions isr*() available,
+ // we can only support a limited number of devices simultaneously
+ // On some devices, notably most Arduinos, the interrupt pin passed in is actually the
+ // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
+ // yourself based on knowledge of what Arduino board you are running on.
+ if (_myInterruptIndex == 0xff)
+ {
+ // First run, no interrupt allocated yet
+ if (_interruptCount <= RH_RF22_NUM_INTERRUPTS)
+ _myInterruptIndex = _interruptCount++;
+ else
+ return false; // Too many devices, not enough interrupt vectors
+ }
+ _deviceForInterrupt[_myInterruptIndex] = this;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ if (_myInterruptIndex == 0)
+ _interruptPin.fall(&isr0);
+ else if (_myInterruptIndex == 1)
+ _interruptPin.fall(&isr1);
+ else if (_myInterruptIndex == 2)
+ _interruptPin.fall(&isr2);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#else
+ if (_myInterruptIndex == 0)
+ attachInterrupt(interruptNumber, isr0, FALLING);
+ else if (_myInterruptIndex == 1)
+ attachInterrupt(interruptNumber, isr1, FALLING);
+ else if (_myInterruptIndex == 2)
+ attachInterrupt(interruptNumber, isr2, FALLING);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#endif
+
+ setModeIdle();
+
+ clearTxBuf();
+ clearRxBuf();
+
+ // Most of these are the POR default
+ spiWrite(RH_RF22_REG_7D_TX_FIFO_CONTROL2, RH_RF22_TXFFAEM_THRESHOLD);
+ spiWrite(RH_RF22_REG_7E_RX_FIFO_CONTROL, RH_RF22_RXFFAFULL_THRESHOLD);
+ spiWrite(RH_RF22_REG_30_DATA_ACCESS_CONTROL, RH_RF22_ENPACRX | RH_RF22_ENPACTX | RH_RF22_ENCRC | (_polynomial & RH_RF22_CRC));
+
+ // Configure the message headers
+ // Here we set up the standard packet format for use by the RH_RF22 library
+ // 8 nibbles preamble
+ // 2 SYNC words 2d, d4
+ // Header length 4 (to, from, id, flags)
+ // 1 octet of data length (0 to 255)
+ // 0 to 255 octets data
+ // 2 CRC octets as CRC16(IBM), computed on the header, length and data
+ // On reception the to address is check for validity against RH_RF22_REG_3F_CHECK_HEADER3
+ // or the broadcast address of 0xff
+ // If no changes are made after this, the transmitted
+ // to address will be 0xff, the from address will be 0xff
+ // and all such messages will be accepted. This permits the out-of the box
+ // RH_RF22 config to act as an unaddresed, unreliable datagram service
+ spiWrite(RH_RF22_REG_32_HEADER_CONTROL1, RH_RF22_BCEN_HEADER3 | RH_RF22_HDCH_HEADER3);
+ spiWrite(RH_RF22_REG_33_HEADER_CONTROL2, RH_RF22_HDLEN_4 | RH_RF22_SYNCLEN_2);
+
+ setPreambleLength(8);
+ uint8_t syncwords[] = { 0x2d, 0xd4 };
+ setSyncWords(syncwords, sizeof(syncwords));
+ setPromiscuous(false);
+
+ // Set some defaults. An innocuous ISM frequency, and reasonable pull-in
+ setFrequency(434.0, 0.05);
+// setFrequency(900.0);
+ // Some slow, reliable default speed and modulation
+ setModemConfig(FSK_Rb2_4Fd36);
+// setModemConfig(FSK_Rb125Fd125);
+ setGpioReversed(false);
+ // Lowish power
+ setTxPower(RH_RF22_TXPOW_8DBM);
+
+ return true;
+}
+
+// C++ level interrupt handler for this instance
+void RH_RF22::handleInterrupt()
+{
+ uint8_t _lastInterruptFlags[2];
+ // Read the interrupt flags which clears the interrupt
+ spiBurstRead(RH_RF22_REG_03_INTERRUPT_STATUS1, _lastInterruptFlags, 2);
+
+#if 0
+ // DEVELOPER TESTING ONLY
+ // Caution: Serial printing in this interrupt routine can cause mysterious crashes
+ Serial.print("interrupt ");
+ Serial.print(_lastInterruptFlags[0], HEX);
+ Serial.print(" ");
+ Serial.println(_lastInterruptFlags[1], HEX);
+ if (_lastInterruptFlags[0] == 0 && _lastInterruptFlags[1] == 0)
+ Serial.println("FUNNY: no interrupt!");
+#endif
+
+#if 0
+ // DEVELOPER TESTING ONLY
+ // TESTING: fake an RH_RF22_IFFERROR
+ static int counter = 0;
+ if (_lastInterruptFlags[0] & RH_RF22_IPKSENT && counter++ == 10)
+ {
+ _lastInterruptFlags[0] = RH_RF22_IFFERROR;
+ counter = 0;
+ }
+#endif
+
+ if (_lastInterruptFlags[0] & RH_RF22_IFFERROR)
+ {
+ resetFifos(); // Clears the interrupt
+ if (_mode == RHModeTx)
+ restartTransmit();
+ else if (_mode == RHModeRx)
+ clearRxBuf();
+// Serial.println("IFFERROR");
+ }
+ // Caution, any delay here may cause a FF underflow or overflow
+ if (_lastInterruptFlags[0] & RH_RF22_ITXFFAEM)
+ {
+ // See if more data has to be loaded into the Tx FIFO
+ sendNextFragment();
+// Serial.println("ITXFFAEM");
+ }
+ if (_lastInterruptFlags[0] & RH_RF22_IRXFFAFULL)
+ {
+ // Caution, any delay here may cause a FF overflow
+ // Read some data from the Rx FIFO
+ readNextFragment();
+// Serial.println("IRXFFAFULL");
+ }
+ if (_lastInterruptFlags[0] & RH_RF22_IEXT)
+ {
+ // This is not enabled by the base code, but users may want to enable it
+ handleExternalInterrupt();
+// Serial.println("IEXT");
+ }
+ if (_lastInterruptFlags[1] & RH_RF22_IWUT)
+ {
+ // This is not enabled by the base code, but users may want to enable it
+ handleWakeupTimerInterrupt();
+// Serial.println("IWUT");
+ }
+ if (_lastInterruptFlags[0] & RH_RF22_IPKSENT)
+ {
+// Serial.println("IPKSENT");
+ _txGood++;
+ // Transmission does not automatically clear the tx buffer.
+ // Could retransmit if we wanted
+ // RH_RF22 transitions automatically to Idle
+ _mode = RHModeIdle;
+ }
+ if (_lastInterruptFlags[0] & RH_RF22_IPKVALID)
+ {
+ uint8_t len = spiRead(RH_RF22_REG_4B_RECEIVED_PACKET_LENGTH);
+// Serial.println("IPKVALID");
+
+ // May have already read one or more fragments
+ // Get any remaining unread octets, based on the expected length
+ // First make sure we dont overflow the buffer in the case of a stupid length
+ // or partial bad receives
+ if ( len > RH_RF22_MAX_MESSAGE_LEN
+ || len < _bufLen)
+ {
+ _rxBad++;
+ _mode = RHModeIdle;
+ clearRxBuf();
+ return; // Hmmm receiver buffer overflow.
+ }
+
+ spiBurstRead(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, len - _bufLen);
+ _rxHeaderTo = spiRead(RH_RF22_REG_47_RECEIVED_HEADER3);
+ _rxHeaderFrom = spiRead(RH_RF22_REG_48_RECEIVED_HEADER2);
+ _rxHeaderId = spiRead(RH_RF22_REG_49_RECEIVED_HEADER1);
+ _rxHeaderFlags = spiRead(RH_RF22_REG_4A_RECEIVED_HEADER0);
+ _rxGood++;
+ _bufLen = len;
+ _mode = RHModeIdle;
+ _rxBufValid = true;
+ }
+ if (_lastInterruptFlags[0] & RH_RF22_ICRCERROR)
+ {
+// Serial.println("ICRCERR");
+ _rxBad++;
+ clearRxBuf();
+ resetRxFifo();
+ _mode = RHModeIdle;
+ setModeRx(); // Keep trying
+ }
+ if (_lastInterruptFlags[1] & RH_RF22_IPREAVAL)
+ {
+// Serial.println("IPREAVAL");
+ _lastRssi = (int8_t)(-120 + ((spiRead(RH_RF22_REG_26_RSSI) / 2)));
+ _lastPreambleTime = millis();
+ resetRxFifo();
+ clearRxBuf();
+ }
+}
+
+// These are low level functions that call the interrupt handler for the correct
+// instance of RH_RF22.
+// 3 interrupts allows us to have 3 different devices
+void RH_RF22::isr0()
+{
+ if (_deviceForInterrupt[0])
+ _deviceForInterrupt[0]->handleInterrupt();
+}
+void RH_RF22::isr1()
+{
+ if (_deviceForInterrupt[1])
+ _deviceForInterrupt[1]->handleInterrupt();
+}
+void RH_RF22::isr2()
+{
+ if (_deviceForInterrupt[2])
+ _deviceForInterrupt[2]->handleInterrupt();
+}
+
+void RH_RF22::reset()
+{
+ spiWrite(RH_RF22_REG_07_OPERATING_MODE1, RH_RF22_SWRES);
+ // Wait for it to settle
+ delay(1); // SWReset time is nominally 100usec
+}
+
+uint8_t RH_RF22::statusRead()
+{
+ return spiRead(RH_RF22_REG_02_DEVICE_STATUS);
+}
+
+uint8_t RH_RF22::adcRead(uint8_t adcsel,
+ uint8_t adcref ,
+ uint8_t adcgain,
+ uint8_t adcoffs)
+{
+ uint8_t configuration = adcsel | adcref | (adcgain & RH_RF22_ADCGAIN);
+ spiWrite(RH_RF22_REG_0F_ADC_CONFIGURATION, configuration | RH_RF22_ADCSTART);
+ spiWrite(RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET, adcoffs);
+
+ // Conversion time is nominally 305usec
+ // Wait for the DONE bit
+ while (!(spiRead(RH_RF22_REG_0F_ADC_CONFIGURATION) & RH_RF22_ADCDONE))
+ ;
+ // Return the value
+ return spiRead(RH_RF22_REG_11_ADC_VALUE);
+}
+
+uint8_t RH_RF22::temperatureRead(uint8_t tsrange, uint8_t tvoffs)
+{
+ spiWrite(RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION, tsrange | RH_RF22_ENTSOFFS);
+ spiWrite(RH_RF22_REG_13_TEMPERATURE_VALUE_OFFSET, tvoffs);
+ return adcRead(RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR | RH_RF22_ADCREF_BANDGAP_VOLTAGE);
+}
+
+uint16_t RH_RF22::wutRead()
+{
+ uint8_t buf[2];
+ spiBurstRead(RH_RF22_REG_17_WAKEUP_TIMER_VALUE1, buf, 2);
+ return ((uint16_t)buf[0] << 8) | buf[1]; // Dont rely on byte order
+}
+
+// RFM-22 doc appears to be wrong: WUT for wtm = 10000, r, = 0, d = 0 is about 1 sec
+void RH_RF22::setWutPeriod(uint16_t wtm, uint8_t wtr, uint8_t wtd)
+{
+ uint8_t period[3];
+
+ period[0] = ((wtr & 0xf) << 2) | (wtd & 0x3);
+ period[1] = wtm >> 8;
+ period[2] = wtm & 0xff;
+ spiBurstWrite(RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1, period, sizeof(period));
+}
+
+// Returns true if centre + (fhch * fhs) is within limits
+// Caution, different versions of the RH_RF22 support different max freq
+// so YMMV
+bool RH_RF22::setFrequency(float centre, float afcPullInRange)
+{
+ uint8_t fbsel = RH_RF22_SBSEL;
+ uint8_t afclimiter;
+ if (centre < 240.0 || centre > 960.0) // 930.0 for early silicon
+ return false;
+ if (centre >= 480.0)
+ {
+ if (afcPullInRange < 0.0 || afcPullInRange > 0.318750)
+ return false;
+ centre /= 2;
+ fbsel |= RH_RF22_HBSEL;
+ afclimiter = afcPullInRange * 1000000.0 / 1250.0;
+ }
+ else
+ {
+ if (afcPullInRange < 0.0 || afcPullInRange > 0.159375)
+ return false;
+ afclimiter = afcPullInRange * 1000000.0 / 625.0;
+ }
+ centre /= 10.0;
+ float integerPart = floor(centre);
+ float fractionalPart = centre - integerPart;
+
+ uint8_t fb = (uint8_t)integerPart - 24; // Range 0 to 23
+ fbsel |= fb;
+ uint16_t fc = fractionalPart * 64000;
+ spiWrite(RH_RF22_REG_73_FREQUENCY_OFFSET1, 0); // REVISIT
+ spiWrite(RH_RF22_REG_74_FREQUENCY_OFFSET2, 0);
+ spiWrite(RH_RF22_REG_75_FREQUENCY_BAND_SELECT, fbsel);
+ spiWrite(RH_RF22_REG_76_NOMINAL_CARRIER_FREQUENCY1, fc >> 8);
+ spiWrite(RH_RF22_REG_77_NOMINAL_CARRIER_FREQUENCY0, fc & 0xff);
+ spiWrite(RH_RF22_REG_2A_AFC_LIMITER, afclimiter);
+ return !(statusRead() & RH_RF22_FREQERR);
+}
+
+// Step size in 10kHz increments
+// Returns true if centre + (fhch * fhs) is within limits
+bool RH_RF22::setFHStepSize(uint8_t fhs)
+{
+ spiWrite(RH_RF22_REG_7A_FREQUENCY_HOPPING_STEP_SIZE, fhs);
+ return !(statusRead() & RH_RF22_FREQERR);
+}
+
+// Adds fhch * fhs to centre frequency
+// Returns true if centre + (fhch * fhs) is within limits
+bool RH_RF22::setFHChannel(uint8_t fhch)
+{
+ spiWrite(RH_RF22_REG_79_FREQUENCY_HOPPING_CHANNEL_SELECT, fhch);
+ return !(statusRead() & RH_RF22_FREQERR);
+}
+
+uint8_t RH_RF22::rssiRead()
+{
+ return spiRead(RH_RF22_REG_26_RSSI);
+}
+
+uint8_t RH_RF22::ezmacStatusRead()
+{
+ return spiRead(RH_RF22_REG_31_EZMAC_STATUS);
+}
+
+void RH_RF22::setOpMode(uint8_t mode)
+{
+ spiWrite(RH_RF22_REG_07_OPERATING_MODE1, mode);
+}
+
+void RH_RF22::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ setOpMode(_idleMode);
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_RF22::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ setOpMode(0);
+ _mode = RHModeSleep;
+ }
+ return true;
+}
+
+void RH_RF22::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ setOpMode(_idleMode | RH_RF22_RXON);
+ _mode = RHModeRx;
+ }
+}
+
+void RH_RF22::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ setOpMode(_idleMode | RH_RF22_TXON);
+ // Hmmm, if you dont clear the RX FIFO here, then it appears that going
+ // to transmit mode in the middle of a receive can corrupt the
+ // RX FIFO
+ resetRxFifo();
+ _mode = RHModeTx;
+ }
+}
+
+void RH_RF22::setTxPower(uint8_t power)
+{
+ spiWrite(RH_RF22_REG_6D_TX_POWER, power | RH_RF22_LNA_SW); // On RF23, LNA_SW must be set.
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF22::setModemRegisters(const ModemConfig* config)
+{
+ spiWrite(RH_RF22_REG_1C_IF_FILTER_BANDWIDTH, config->reg_1c);
+ spiWrite(RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE, config->reg_1f);
+ spiBurstWrite(RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE, &config->reg_20, 6);
+ spiBurstWrite(RH_RF22_REG_2C_OOK_COUNTER_VALUE_1, &config->reg_2c, 3);
+ spiWrite(RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING, config->reg_58);
+ spiWrite(RH_RF22_REG_69_AGC_OVERRIDE1, config->reg_69);
+ spiBurstWrite(RH_RF22_REG_6E_TX_DATA_RATE1, &config->reg_6e, 5);
+}
+
+// Set one of the canned FSK Modem configs
+// Returns true if its a valid choice
+bool RH_RF22::setModemConfig(ModemConfigChoice index)
+{
+ if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
+ return false;
+
+ RH_RF22::ModemConfig cfg;
+ memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF22::ModemConfig));
+ setModemRegisters(&cfg);
+
+ return true;
+}
+
+// REVISIT: top bit is in Header Control 2 0x33
+void RH_RF22::setPreambleLength(uint8_t nibbles)
+{
+ spiWrite(RH_RF22_REG_34_PREAMBLE_LENGTH, nibbles);
+}
+
+// Caution doesnt set sync word len in Header Control 2 0x33
+void RH_RF22::setSyncWords(const uint8_t* syncWords, uint8_t len)
+{
+ spiBurstWrite(RH_RF22_REG_36_SYNC_WORD3, syncWords, len);
+}
+
+void RH_RF22::clearRxBuf()
+{
+ ATOMIC_BLOCK_START;
+ _bufLen = 0;
+ _rxBufValid = false;
+ ATOMIC_BLOCK_END;
+}
+
+bool RH_RF22::available()
+{
+ if (!_rxBufValid)
+ {
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx(); // Make sure we are receiving
+ }
+ return _rxBufValid;
+}
+
+bool RH_RF22::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+
+ if (buf && len)
+ {
+ ATOMIC_BLOCK_START;
+ if (*len > _bufLen)
+ *len = _bufLen;
+ memcpy(buf, _buf, *len);
+ ATOMIC_BLOCK_END;
+ }
+ clearRxBuf();
+// printBuffer("recv:", buf, *len);
+ return true;
+}
+
+void RH_RF22::clearTxBuf()
+{
+ ATOMIC_BLOCK_START;
+ _bufLen = 0;
+ _txBufSentIndex = 0;
+ ATOMIC_BLOCK_END;
+}
+
+void RH_RF22::startTransmit()
+{
+ sendNextFragment(); // Actually the first fragment
+ spiWrite(RH_RF22_REG_3E_PACKET_LENGTH, _bufLen); // Total length that will be sent
+ setModeTx(); // Start the transmitter, turns off the receiver
+}
+
+// Restart the transmission of a packet that had a problem
+void RH_RF22::restartTransmit()
+{
+ _mode = RHModeIdle;
+ _txBufSentIndex = 0;
+// Serial.println("Restart");
+ startTransmit();
+}
+
+bool RH_RF22::send(const uint8_t* data, uint8_t len)
+{
+ bool ret = true;
+ waitPacketSent();
+ ATOMIC_BLOCK_START;
+ spiWrite(RH_RF22_REG_3A_TRANSMIT_HEADER3, _txHeaderTo);
+ spiWrite(RH_RF22_REG_3B_TRANSMIT_HEADER2, _txHeaderFrom);
+ spiWrite(RH_RF22_REG_3C_TRANSMIT_HEADER1, _txHeaderId);
+ spiWrite(RH_RF22_REG_3D_TRANSMIT_HEADER0, _txHeaderFlags);
+ if (!fillTxBuf(data, len))
+ ret = false;
+ else
+ startTransmit();
+ ATOMIC_BLOCK_END;
+// printBuffer("send:", data, len);
+ return ret;
+}
+
+bool RH_RF22::fillTxBuf(const uint8_t* data, uint8_t len)
+{
+ clearTxBuf();
+ if (!len)
+ return false;
+ return appendTxBuf(data, len);
+}
+
+bool RH_RF22::appendTxBuf(const uint8_t* data, uint8_t len)
+{
+ if (((uint16_t)_bufLen + len) > RH_RF22_MAX_MESSAGE_LEN)
+ return false;
+ ATOMIC_BLOCK_START;
+ memcpy(_buf + _bufLen, data, len);
+ _bufLen += len;
+ ATOMIC_BLOCK_END;
+// printBuffer("txbuf:", _buf, _bufLen);
+ return true;
+}
+
+// Assumption: there is currently <= RH_RF22_TXFFAEM_THRESHOLD bytes in the Tx FIFO
+void RH_RF22::sendNextFragment()
+{
+ if (_txBufSentIndex < _bufLen)
+ {
+ // Some left to send?
+ uint8_t len = _bufLen - _txBufSentIndex;
+ // But dont send too much
+ if (len > (RH_RF22_FIFO_SIZE - RH_RF22_TXFFAEM_THRESHOLD - 1))
+ len = (RH_RF22_FIFO_SIZE - RH_RF22_TXFFAEM_THRESHOLD - 1);
+ spiBurstWrite(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _txBufSentIndex, len);
+// printBuffer("frag:", _buf + _txBufSentIndex, len);
+ _txBufSentIndex += len;
+ }
+}
+
+// Assumption: there are at least RH_RF22_RXFFAFULL_THRESHOLD in the RX FIFO
+// That means it should only be called after a RXFFAFULL interrupt
+void RH_RF22::readNextFragment()
+{
+ if (((uint16_t)_bufLen + RH_RF22_RXFFAFULL_THRESHOLD) > RH_RF22_MAX_MESSAGE_LEN)
+ return; // Hmmm receiver overflow. Should never occur
+
+ // Read the RH_RF22_RXFFAFULL_THRESHOLD octets that should be there
+ spiBurstRead(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, RH_RF22_RXFFAFULL_THRESHOLD);
+ _bufLen += RH_RF22_RXFFAFULL_THRESHOLD;
+}
+
+// Clear the FIFOs
+void RH_RF22::resetFifos()
+{
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRRX | RH_RF22_FFCLRTX);
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0);
+}
+
+// Clear the Rx FIFO
+void RH_RF22::resetRxFifo()
+{
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRRX);
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0);
+}
+
+// CLear the TX FIFO
+void RH_RF22::resetTxFifo()
+{
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRTX);
+ spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0);
+}
+
+// Default implmentation does nothing. Override if you wish
+void RH_RF22::handleExternalInterrupt()
+{
+}
+
+// Default implmentation does nothing. Override if you wish
+void RH_RF22::handleWakeupTimerInterrupt()
+{
+}
+
+void RH_RF22::setPromiscuous(bool promiscuous)
+{
+ RHSPIDriver::setPromiscuous(promiscuous);
+ spiWrite(RH_RF22_REG_43_HEADER_ENABLE3, promiscuous ? 0x00 : 0xff);
+}
+
+bool RH_RF22::setCRCPolynomial(CRCPolynomial polynomial)
+{
+ if (polynomial >= CRC_CCITT &&
+ polynomial <= CRC_Biacheva)
+ {
+ _polynomial = polynomial;
+ return true;
+ }
+ else
+ return false;
+}
+
+uint8_t RH_RF22::maxMessageLength()
+{
+ return RH_RF22_MAX_MESSAGE_LEN;
+}
+
+void RH_RF22::setThisAddress(uint8_t thisAddress)
+{
+ RHSPIDriver::setThisAddress(thisAddress);
+ spiWrite(RH_RF22_REG_3F_CHECK_HEADER3, thisAddress);
+}
+
+uint32_t RH_RF22::getLastPreambleTime()
+{
+ return _lastPreambleTime;
+}
+
+void RH_RF22::setGpioReversed(bool gpioReversed)
+{
+ // Ensure the antenna can be switched automatically according to transmit and receive
+ // This assumes GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit
+ // This assumes GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive
+ if (gpioReversed)
+ {
+ // Reversed for HAB-RFM22B-BOA HAB-RFM22B-BO, also Si4432 sold by Dorji.com via Tindie.com.
+ spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x15) ; // RX state
+ spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x12) ; // TX state
+ }
+ else
+ {
+ spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x12) ; // TX state
+ spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x15) ; // RX state
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF22.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,1269 @@
+// RH_RF22.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF22.h,v 1.27 2015/05/17 00:11:26 mikem Exp $
+//
+
+#ifndef RH_RF22_h
+#define RH_RF22_h
+
+#include <RHGenericSPI.h>
+#include <RHSPIDriver.h>
+
+// This is the maximum number of interrupts the library can support
+// Most Arduinos can handle 2, Megas can handle more
+#define RH_RF22_NUM_INTERRUPTS 3
+
+// This is the bit in the SPI address that marks it as a write
+#define RH_RF22_SPI_WRITE_MASK 0x80
+
+// This is the maximum message length that can be supported by this library. Limited by
+// the single message length octet in the header.
+// Yes, 255 is correct even though the FIFO size in the RF22 is only
+// 64 octets. We use interrupts to refill the Tx FIFO during transmission and to empty the
+// Rx FIFO during reception
+// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
+#ifndef RH_RF22_MAX_MESSAGE_LEN
+//#define RH_RF22_MAX_MESSAGE_LEN 255
+#define RH_RF22_MAX_MESSAGE_LEN 50
+#endif
+
+// Max number of octets the RF22 Rx and Tx FIFOs can hold
+#define RH_RF22_FIFO_SIZE 64
+
+// These values we set for FIFO thresholds (4, 55) are actually the same as the POR values
+#define RH_RF22_TXFFAEM_THRESHOLD 4
+#define RH_RF22_RXFFAFULL_THRESHOLD 55
+
+// Number of registers to be passed to setModemConfig(). Obsolete.
+#define RH_RF22_NUM_MODEM_CONFIG_REGS 18
+
+// Register names
+#define RH_RF22_REG_00_DEVICE_TYPE 0x00
+#define RH_RF22_REG_01_VERSION_CODE 0x01
+#define RH_RF22_REG_02_DEVICE_STATUS 0x02
+#define RH_RF22_REG_03_INTERRUPT_STATUS1 0x03
+#define RH_RF22_REG_04_INTERRUPT_STATUS2 0x04
+#define RH_RF22_REG_05_INTERRUPT_ENABLE1 0x05
+#define RH_RF22_REG_06_INTERRUPT_ENABLE2 0x06
+#define RH_RF22_REG_07_OPERATING_MODE1 0x07
+#define RH_RF22_REG_08_OPERATING_MODE2 0x08
+#define RH_RF22_REG_09_OSCILLATOR_LOAD_CAPACITANCE 0x09
+#define RH_RF22_REG_0A_UC_OUTPUT_CLOCK 0x0a
+#define RH_RF22_REG_0B_GPIO_CONFIGURATION0 0x0b
+#define RH_RF22_REG_0C_GPIO_CONFIGURATION1 0x0c
+#define RH_RF22_REG_0D_GPIO_CONFIGURATION2 0x0d
+#define RH_RF22_REG_0E_IO_PORT_CONFIGURATION 0x0e
+#define RH_RF22_REG_0F_ADC_CONFIGURATION 0x0f
+#define RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET 0x10
+#define RH_RF22_REG_11_ADC_VALUE 0x11
+#define RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION 0x12
+#define RH_RF22_REG_13_TEMPERATURE_VALUE_OFFSET 0x13
+#define RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1 0x14
+#define RH_RF22_REG_15_WAKEUP_TIMER_PERIOD2 0x15
+#define RH_RF22_REG_16_WAKEUP_TIMER_PERIOD3 0x16
+#define RH_RF22_REG_17_WAKEUP_TIMER_VALUE1 0x17
+#define RH_RF22_REG_18_WAKEUP_TIMER_VALUE2 0x18
+#define RH_RF22_REG_19_LDC_MODE_DURATION 0x19
+#define RH_RF22_REG_1A_LOW_BATTERY_DETECTOR_THRESHOLD 0x1a
+#define RH_RF22_REG_1B_BATTERY_VOLTAGE_LEVEL 0x1b
+#define RH_RF22_REG_1C_IF_FILTER_BANDWIDTH 0x1c
+#define RH_RF22_REG_1D_AFC_LOOP_GEARSHIFT_OVERRIDE 0x1d
+#define RH_RF22_REG_1E_AFC_TIMING_CONTROL 0x1e
+#define RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE 0x1f
+#define RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE 0x20
+#define RH_RF22_REG_21_CLOCK_RECOVERY_OFFSET2 0x21
+#define RH_RF22_REG_22_CLOCK_RECOVERY_OFFSET1 0x22
+#define RH_RF22_REG_23_CLOCK_RECOVERY_OFFSET0 0x23
+#define RH_RF22_REG_24_CLOCK_RECOVERY_TIMING_LOOP_GAIN1 0x24
+#define RH_RF22_REG_25_CLOCK_RECOVERY_TIMING_LOOP_GAIN0 0x25
+#define RH_RF22_REG_26_RSSI 0x26
+#define RH_RF22_REG_27_RSSI_THRESHOLD 0x27
+#define RH_RF22_REG_28_ANTENNA_DIVERSITY1 0x28
+#define RH_RF22_REG_29_ANTENNA_DIVERSITY2 0x29
+#define RH_RF22_REG_2A_AFC_LIMITER 0x2a
+#define RH_RF22_REG_2B_AFC_CORRECTION_READ 0x2b
+#define RH_RF22_REG_2C_OOK_COUNTER_VALUE_1 0x2c
+#define RH_RF22_REG_2D_OOK_COUNTER_VALUE_2 0x2d
+#define RH_RF22_REG_2E_SLICER_PEAK_HOLD 0x2e
+#define RH_RF22_REG_30_DATA_ACCESS_CONTROL 0x30
+#define RH_RF22_REG_31_EZMAC_STATUS 0x31
+#define RH_RF22_REG_32_HEADER_CONTROL1 0x32
+#define RH_RF22_REG_33_HEADER_CONTROL2 0x33
+#define RH_RF22_REG_34_PREAMBLE_LENGTH 0x34
+#define RH_RF22_REG_35_PREAMBLE_DETECTION_CONTROL1 0x35
+#define RH_RF22_REG_36_SYNC_WORD3 0x36
+#define RH_RF22_REG_37_SYNC_WORD2 0x37
+#define RH_RF22_REG_38_SYNC_WORD1 0x38
+#define RH_RF22_REG_39_SYNC_WORD0 0x39
+#define RH_RF22_REG_3A_TRANSMIT_HEADER3 0x3a
+#define RH_RF22_REG_3B_TRANSMIT_HEADER2 0x3b
+#define RH_RF22_REG_3C_TRANSMIT_HEADER1 0x3c
+#define RH_RF22_REG_3D_TRANSMIT_HEADER0 0x3d
+#define RH_RF22_REG_3E_PACKET_LENGTH 0x3e
+#define RH_RF22_REG_3F_CHECK_HEADER3 0x3f
+#define RH_RF22_REG_40_CHECK_HEADER2 0x40
+#define RH_RF22_REG_41_CHECK_HEADER1 0x41
+#define RH_RF22_REG_42_CHECK_HEADER0 0x42
+#define RH_RF22_REG_43_HEADER_ENABLE3 0x43
+#define RH_RF22_REG_44_HEADER_ENABLE2 0x44
+#define RH_RF22_REG_45_HEADER_ENABLE1 0x45
+#define RH_RF22_REG_46_HEADER_ENABLE0 0x46
+#define RH_RF22_REG_47_RECEIVED_HEADER3 0x47
+#define RH_RF22_REG_48_RECEIVED_HEADER2 0x48
+#define RH_RF22_REG_49_RECEIVED_HEADER1 0x49
+#define RH_RF22_REG_4A_RECEIVED_HEADER0 0x4a
+#define RH_RF22_REG_4B_RECEIVED_PACKET_LENGTH 0x4b
+#define RH_RF22_REG_50_ANALOG_TEST_BUS_SELECT 0x50
+#define RH_RF22_REG_51_DIGITAL_TEST_BUS_SELECT 0x51
+#define RH_RF22_REG_52_TX_RAMP_CONTROL 0x52
+#define RH_RF22_REG_53_PLL_TUNE_TIME 0x53
+#define RH_RF22_REG_55_CALIBRATION_CONTROL 0x55
+#define RH_RF22_REG_56_MODEM_TEST 0x56
+#define RH_RF22_REG_57_CHARGE_PUMP_TEST 0x57
+#define RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING 0x58
+#define RH_RF22_REG_59_DIVIDER_CURRENT_TRIMMING 0x59
+#define RH_RF22_REG_5A_VCO_CURRENT_TRIMMING 0x5a
+#define RH_RF22_REG_5B_VCO_CALIBRATION 0x5b
+#define RH_RF22_REG_5C_SYNTHESIZER_TEST 0x5c
+#define RH_RF22_REG_5D_BLOCK_ENABLE_OVERRIDE1 0x5d
+#define RH_RF22_REG_5E_BLOCK_ENABLE_OVERRIDE2 0x5e
+#define RH_RF22_REG_5F_BLOCK_ENABLE_OVERRIDE3 0x5f
+#define RH_RF22_REG_60_CHANNEL_FILTER_COEFFICIENT_ADDRESS 0x60
+#define RH_RF22_REG_61_CHANNEL_FILTER_COEFFICIENT_VALUE 0x61
+#define RH_RF22_REG_62_CRYSTAL_OSCILLATOR_POR_CONTROL 0x62
+#define RH_RF22_REG_63_RC_OSCILLATOR_COARSE_CALIBRATION 0x63
+#define RH_RF22_REG_64_RC_OSCILLATOR_FINE_CALIBRATION 0x64
+#define RH_RF22_REG_65_LDO_CONTROL_OVERRIDE 0x65
+#define RH_RF22_REG_66_LDO_LEVEL_SETTINGS 0x66
+#define RH_RF22_REG_67_DELTA_SIGMA_ADC_TUNING1 0x67
+#define RH_RF22_REG_68_DELTA_SIGMA_ADC_TUNING2 0x68
+#define RH_RF22_REG_69_AGC_OVERRIDE1 0x69
+#define RH_RF22_REG_6A_AGC_OVERRIDE2 0x6a
+#define RH_RF22_REG_6B_GFSK_FIR_FILTER_COEFFICIENT_ADDRESS 0x6b
+#define RH_RF22_REG_6C_GFSK_FIR_FILTER_COEFFICIENT_VALUE 0x6c
+#define RH_RF22_REG_6D_TX_POWER 0x6d
+#define RH_RF22_REG_6E_TX_DATA_RATE1 0x6e
+#define RH_RF22_REG_6F_TX_DATA_RATE0 0x6f
+#define RH_RF22_REG_70_MODULATION_CONTROL1 0x70
+#define RH_RF22_REG_71_MODULATION_CONTROL2 0x71
+#define RH_RF22_REG_72_FREQUENCY_DEVIATION 0x72
+#define RH_RF22_REG_73_FREQUENCY_OFFSET1 0x73
+#define RH_RF22_REG_74_FREQUENCY_OFFSET2 0x74
+#define RH_RF22_REG_75_FREQUENCY_BAND_SELECT 0x75
+#define RH_RF22_REG_76_NOMINAL_CARRIER_FREQUENCY1 0x76
+#define RH_RF22_REG_77_NOMINAL_CARRIER_FREQUENCY0 0x77
+#define RH_RF22_REG_79_FREQUENCY_HOPPING_CHANNEL_SELECT 0x79
+#define RH_RF22_REG_7A_FREQUENCY_HOPPING_STEP_SIZE 0x7a
+#define RH_RF22_REG_7C_TX_FIFO_CONTROL1 0x7c
+#define RH_RF22_REG_7D_TX_FIFO_CONTROL2 0x7d
+#define RH_RF22_REG_7E_RX_FIFO_CONTROL 0x7e
+#define RH_RF22_REG_7F_FIFO_ACCESS 0x7f
+
+// These register masks etc are named wherever possible
+// corresponding to the bit and field names in the RF-22 Manual
+// RH_RF22_REG_00_DEVICE_TYPE 0x00
+#define RH_RF22_DEVICE_TYPE_RX_TRX 0x08
+#define RH_RF22_DEVICE_TYPE_TX 0x07
+
+// RH_RF22_REG_02_DEVICE_STATUS 0x02
+#define RH_RF22_FFOVL 0x80
+#define RH_RF22_FFUNFL 0x40
+#define RH_RF22_RXFFEM 0x20
+#define RH_RF22_HEADERR 0x10
+#define RH_RF22_FREQERR 0x08
+#define RH_RF22_LOCKDET 0x04
+#define RH_RF22_CPS 0x03
+#define RH_RF22_CPS_IDLE 0x00
+#define RH_RF22_CPS_RX 0x01
+#define RH_RF22_CPS_TX 0x10
+
+// RH_RF22_REG_03_INTERRUPT_STATUS1 0x03
+#define RH_RF22_IFFERROR 0x80
+#define RH_RF22_ITXFFAFULL 0x40
+#define RH_RF22_ITXFFAEM 0x20
+#define RH_RF22_IRXFFAFULL 0x10
+#define RH_RF22_IEXT 0x08
+#define RH_RF22_IPKSENT 0x04
+#define RH_RF22_IPKVALID 0x02
+#define RH_RF22_ICRCERROR 0x01
+
+// RH_RF22_REG_04_INTERRUPT_STATUS2 0x04
+#define RH_RF22_ISWDET 0x80
+#define RH_RF22_IPREAVAL 0x40
+#define RH_RF22_IPREAINVAL 0x20
+#define RH_RF22_IRSSI 0x10
+#define RH_RF22_IWUT 0x08
+#define RH_RF22_ILBD 0x04
+#define RH_RF22_ICHIPRDY 0x02
+#define RH_RF22_IPOR 0x01
+
+// RH_RF22_REG_05_INTERRUPT_ENABLE1 0x05
+#define RH_RF22_ENFFERR 0x80
+#define RH_RF22_ENTXFFAFULL 0x40
+#define RH_RF22_ENTXFFAEM 0x20
+#define RH_RF22_ENRXFFAFULL 0x10
+#define RH_RF22_ENEXT 0x08
+#define RH_RF22_ENPKSENT 0x04
+#define RH_RF22_ENPKVALID 0x02
+#define RH_RF22_ENCRCERROR 0x01
+
+// RH_RF22_REG_06_INTERRUPT_ENABLE2 0x06
+#define RH_RF22_ENSWDET 0x80
+#define RH_RF22_ENPREAVAL 0x40
+#define RH_RF22_ENPREAINVAL 0x20
+#define RH_RF22_ENRSSI 0x10
+#define RH_RF22_ENWUT 0x08
+#define RH_RF22_ENLBDI 0x04
+#define RH_RF22_ENCHIPRDY 0x02
+#define RH_RF22_ENPOR 0x01
+
+// RH_RF22_REG_07_OPERATING_MODE 0x07
+#define RH_RF22_SWRES 0x80
+#define RH_RF22_ENLBD 0x40
+#define RH_RF22_ENWT 0x20
+#define RH_RF22_X32KSEL 0x10
+#define RH_RF22_TXON 0x08
+#define RH_RF22_RXON 0x04
+#define RH_RF22_PLLON 0x02
+#define RH_RF22_XTON 0x01
+
+// RH_RF22_REG_08_OPERATING_MODE2 0x08
+#define RH_RF22_ANTDIV 0xc0
+#define RH_RF22_RXMPK 0x10
+#define RH_RF22_AUTOTX 0x08
+#define RH_RF22_ENLDM 0x04
+#define RH_RF22_FFCLRRX 0x02
+#define RH_RF22_FFCLRTX 0x01
+
+// RH_RF22_REG_0F_ADC_CONFIGURATION 0x0f
+#define RH_RF22_ADCSTART 0x80
+#define RH_RF22_ADCDONE 0x80
+#define RH_RF22_ADCSEL 0x70
+#define RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR 0x00
+#define RH_RF22_ADCSEL_GPIO0_SINGLE_ENDED 0x10
+#define RH_RF22_ADCSEL_GPIO1_SINGLE_ENDED 0x20
+#define RH_RF22_ADCSEL_GPIO2_SINGLE_ENDED 0x30
+#define RH_RF22_ADCSEL_GPIO0_GPIO1_DIFFERENTIAL 0x40
+#define RH_RF22_ADCSEL_GPIO1_GPIO2_DIFFERENTIAL 0x50
+#define RH_RF22_ADCSEL_GPIO0_GPIO2_DIFFERENTIAL 0x60
+#define RH_RF22_ADCSEL_GND 0x70
+#define RH_RF22_ADCREF 0x0c
+#define RH_RF22_ADCREF_BANDGAP_VOLTAGE 0x00
+#define RH_RF22_ADCREF_VDD_ON_3 0x08
+#define RH_RF22_ADCREF_VDD_ON_2 0x0c
+#define RH_RF22_ADCGAIN 0x03
+
+// RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET 0x10
+#define RH_RF22_ADCOFFS 0x0f
+
+// RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION 0x12
+#define RH_RF22_TSRANGE 0xc0
+#define RH_RF22_TSRANGE_M64_64C 0x00
+#define RH_RF22_TSRANGE_M64_192C 0x40
+#define RH_RF22_TSRANGE_0_128C 0x80
+#define RH_RF22_TSRANGE_M40_216F 0xc0
+#define RH_RF22_ENTSOFFS 0x20
+#define RH_RF22_ENTSTRIM 0x10
+#define RH_RF22_TSTRIM 0x0f
+
+// RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1 0x14
+#define RH_RF22_WTR 0x3c
+#define RH_RF22_WTD 0x03
+
+// RH_RF22_REG_1D_AFC_LOOP_GEARSHIFT_OVERRIDE 0x1d
+#define RH_RF22_AFBCD 0x80
+#define RH_RF22_ENAFC 0x40
+#define RH_RF22_AFCGEARH 0x38
+#define RH_RF22_AFCGEARL 0x07
+
+// RH_RF22_REG_1E_AFC_TIMING_CONTROL 0x1e
+#define RH_RF22_SWAIT_TIMER 0xc0
+#define RH_RF22_SHWAIT 0x38
+#define RH_RF22_ANWAIT 0x07
+
+// RH_RF22_REG_30_DATA_ACCESS_CONTROL 0x30
+#define RH_RF22_ENPACRX 0x80
+#define RH_RF22_MSBFRST 0x00
+#define RH_RF22_LSBFRST 0x40
+#define RH_RF22_CRCHDRS 0x00
+#define RH_RF22_CRCDONLY 0x20
+#define RH_RF22_SKIP2PH 0x10
+#define RH_RF22_ENPACTX 0x08
+#define RH_RF22_ENCRC 0x04
+#define RH_RF22_CRC 0x03
+#define RH_RF22_CRC_CCITT 0x00
+#define RH_RF22_CRC_CRC_16_IBM 0x01
+#define RH_RF22_CRC_IEC_16 0x02
+#define RH_RF22_CRC_BIACHEVA 0x03
+
+// RH_RF22_REG_32_HEADER_CONTROL1 0x32
+#define RH_RF22_BCEN 0xf0
+#define RH_RF22_BCEN_NONE 0x00
+#define RH_RF22_BCEN_HEADER0 0x10
+#define RH_RF22_BCEN_HEADER1 0x20
+#define RH_RF22_BCEN_HEADER2 0x40
+#define RH_RF22_BCEN_HEADER3 0x80
+#define RH_RF22_HDCH 0x0f
+#define RH_RF22_HDCH_NONE 0x00
+#define RH_RF22_HDCH_HEADER0 0x01
+#define RH_RF22_HDCH_HEADER1 0x02
+#define RH_RF22_HDCH_HEADER2 0x04
+#define RH_RF22_HDCH_HEADER3 0x08
+
+// RH_RF22_REG_33_HEADER_CONTROL2 0x33
+#define RH_RF22_HDLEN 0x70
+#define RH_RF22_HDLEN_0 0x00
+#define RH_RF22_HDLEN_1 0x10
+#define RH_RF22_HDLEN_2 0x20
+#define RH_RF22_HDLEN_3 0x30
+#define RH_RF22_HDLEN_4 0x40
+#define RH_RF22_VARPKLEN 0x00
+#define RH_RF22_FIXPKLEN 0x08
+#define RH_RF22_SYNCLEN 0x06
+#define RH_RF22_SYNCLEN_1 0x00
+#define RH_RF22_SYNCLEN_2 0x02
+#define RH_RF22_SYNCLEN_3 0x04
+#define RH_RF22_SYNCLEN_4 0x06
+#define RH_RF22_PREALEN8 0x01
+
+// RH_RF22_REG_6D_TX_POWER 0x6d
+// https://www.sparkfun.com/datasheets/Wireless/General/RFM22B.pdf
+#define RH_RF22_PAPEAKVAL 0x80
+#define RH_RF22_PAPEAKEN 0x40
+#define RH_RF22_PAPEAKLVL 0x30
+#define RH_RF22_PAPEAKLVL6_5 0x00
+#define RH_RF22_PAPEAKLVL7 0x10
+#define RH_RF22_PAPEAKLVL7_5 0x20
+#define RH_RF22_PAPEAKLVL8 0x30
+#define RH_RF22_LNA_SW 0x08
+#define RH_RF22_TXPOW 0x07
+#define RH_RF22_TXPOW_4X31 0x08 // Not used in RFM22B
+// For RFM22B:
+#define RH_RF22_TXPOW_1DBM 0x00
+#define RH_RF22_TXPOW_2DBM 0x01
+#define RH_RF22_TXPOW_5DBM 0x02
+#define RH_RF22_TXPOW_8DBM 0x03
+#define RH_RF22_TXPOW_11DBM 0x04
+#define RH_RF22_TXPOW_14DBM 0x05
+#define RH_RF22_TXPOW_17DBM 0x06
+#define RH_RF22_TXPOW_20DBM 0x07
+// RFM23B only:
+#define RH_RF22_RF23B_TXPOW_M8DBM 0x00 // -8dBm
+#define RH_RF22_RF23B_TXPOW_M5DBM 0x01 // -5dBm
+#define RH_RF22_RF23B_TXPOW_M2DBM 0x02 // -2dBm
+#define RH_RF22_RF23B_TXPOW_1DBM 0x03 // 1dBm
+#define RH_RF22_RF23B_TXPOW_4DBM 0x04 // 4dBm
+#define RH_RF22_RF23B_TXPOW_7DBM 0x05 // 7dBm
+#define RH_RF22_RF23B_TXPOW_10DBM 0x06 // 10dBm
+#define RH_RF22_RF23B_TXPOW_13DBM 0x07 // 13dBm
+// RFM23BP only:
+#define RH_RF22_RF23BP_TXPOW_28DBM 0x05 // 28dBm
+#define RH_RF22_RF23BP_TXPOW_29DBM 0x06 // 29dBm
+#define RH_RF22_RF23BP_TXPOW_30DBM 0x07 // 30dBm
+
+// RH_RF22_REG_71_MODULATION_CONTROL2 0x71
+#define RH_RF22_TRCLK 0xc0
+#define RH_RF22_TRCLK_NONE 0x00
+#define RH_RF22_TRCLK_GPIO 0x40
+#define RH_RF22_TRCLK_SDO 0x80
+#define RH_RF22_TRCLK_NIRQ 0xc0
+#define RH_RF22_DTMOD 0x30
+#define RH_RF22_DTMOD_DIRECT_GPIO 0x00
+#define RH_RF22_DTMOD_DIRECT_SDI 0x10
+#define RH_RF22_DTMOD_FIFO 0x20
+#define RH_RF22_DTMOD_PN9 0x30
+#define RH_RF22_ENINV 0x08
+#define RH_RF22_FD8 0x04
+#define RH_RF22_MODTYP 0x30
+#define RH_RF22_MODTYP_UNMODULATED 0x00
+#define RH_RF22_MODTYP_OOK 0x01
+#define RH_RF22_MODTYP_FSK 0x02
+#define RH_RF22_MODTYP_GFSK 0x03
+
+
+// RH_RF22_REG_75_FREQUENCY_BAND_SELECT 0x75
+#define RH_RF22_SBSEL 0x40
+#define RH_RF22_HBSEL 0x20
+#define RH_RF22_FB 0x1f
+
+// Define this to include Serial printing in diagnostic routines
+#define RH_RF22_HAVE_SERIAL
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_RF22 RH_RF22.h <RH_RF22.h>
+/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF22 and compatible radio transceiver.
+///
+/// Works with RF22, RF23 based radio modules, and compatible chips and modules, including:
+/// - RF22 bare module: http://www.sparkfun.com/products/10153
+/// (Caution, that is a 3.3V part, and requires a 3.3V CPU such as Teensy etc or level shifters)
+/// - RF22 shield: http://www.sparkfun.com/products/11018
+/// - RF22 integrated board http://www.anarduino.com/miniwireless
+/// - RFM23BP bare module: http://www.anarduino.com/details.jsp?pid=130
+/// - Silicon Labs Si4430/31/32 based modules. S4432 is equivalent to RF22. Si4431/30 is equivalent to RF23.
+///
+/// Data based on https://www.sparkfun.com/datasheets/Wireless/General/RFM22B.pdf
+///
+/// \par Overview
+///
+/// This base class provides basic functions for sending and receiving unaddressed,
+/// unreliable datagrams of arbitrary length to 255 octets per packet.
+///
+/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
+/// mesh routers, repeaters, translators etc.
+///
+/// On transmission, the TO and FROM addresses default to 0x00, unless changed by a subclass.
+/// On reception the TO addressed is checked against the node address (defaults to 0x00) or the
+/// broadcast address (which is 0xff). The ID and FLAGS are set to 0, and not checked by this class.
+/// This permits use of the this base RH_RF22 class as an
+/// unaddressed, unreliable datagram service without the use of one the RadioHead Manager classes.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// modulation scheme.
+///
+/// \par Details
+///
+/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF
+/// RF22 and RF23 based radio modules, and compatible chips and modules,
+/// including the RFM22B transceiver module such as
+/// this bare module: http://www.sparkfun.com/products/10153
+/// and this shield: http://www.sparkfun.com/products/11018
+/// and this module: http://www.hoperfusa.com/details.jsp?pid=131
+/// and this integrated board: http://www.anarduino.com/miniwireless
+/// and RF23BP modules such as this http://www.anarduino.com/details.jsp?pid=130
+///
+/// The Hope-RF (http://www.hoperf.com) RFM22B (http://www.hoperf.com/rf_fsk/fsk/RFM22B.htm)
+/// is a low-cost ISM transceiver module. It supports FSK, GFSK, OOK over a wide
+/// range of frequencies and programmable data rates.
+/// Manual can be found at https://www.sparkfun.com/datasheets/Wireless/General/RFM22.PDF
+///
+/// This library provides functions for sending and receiving messages of up to 255 octets on any
+/// frequency supported by the RF22B, in a range of predefined data rates and frequency deviations.
+/// Frequency can be set with 312Hz precision to any frequency from 240.0MHz to 960.0MHz.
+///
+/// Up to 3 RF22B modules can be connected to an Arduino, permitting the construction of translators
+/// and frequency changers, etc.
+///
+/// The following modulation types are suppported with a range of modem configurations for
+/// common data rates and frequency deviations:
+/// - GFSK Gaussian Frequency Shift Keying
+/// - FSK Frequency Shift Keying
+/// - OOK On-Off Keying
+///
+/// Support for other RF22B features such as on-chip temperature measurement, analog-digital
+/// converter, transmitter power control etc is also provided.
+///
+/// Tested on Arduino Diecimila, Uno and Mega with arduino-0021, 1.0.5
+/// on OpenSuSE 13.1 and avr-libc-1.6.1-1.15,
+/// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5.
+/// With HopeRF RFM22 modules that appear to have RF22B chips on board:
+/// - Device Type Code = 0x08 (RX/TRX)
+/// - Version Code = 0x06
+/// Works on Duo. Works with Sparkfun RFM22 Wireless shields. Works with RFM22 modules from http://www.hoperfusa.com/
+/// Works with Arduino 1.0 to at least 1.0.5. Works on Maple, Flymaple, Uno32.
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this Driver must conform to this packet format:
+///
+/// - 8 nibbles (4 octets) PREAMBLE
+/// - 2 octets SYNC 0x2d, 0xd4
+/// - 4 octets HEADER: (TO, FROM, ID, FLAGS)
+/// - 1 octet LENGTH (0 to 255), number of octets in DATA
+/// - 0 to 255 octets DATA
+/// - 2 octets CRC computed with CRC16(IBM), computed on HEADER, LENGTH and DATA
+///
+/// For technical reasons, the message format is not protocol compatible with the
+/// 'HopeRF Radio Transceiver Message Library for Arduino' http://www.airspayce.com/mikem/arduino/HopeRF from the same author. Nor is it compatible with
+/// 'Virtual Wire' http://www.airspayce.com/mikem/arduino/VirtualWire.pdf also from the same author.
+///
+/// \par Connecting RFM-22 to Arduino
+///
+/// If you have the Sparkfun RFM22 Shield (https://www.sparkfun.com/products/11018)
+/// the connections described below are done for you on the shield, no changes required,
+/// just add headers and plug it in to an Arduino (but not and Arduino Mega, see below)
+///
+/// The physical connection between the RF22B and the Arduino requires 3.3V,
+/// the 3 x SPI pins (SCK, SDI, SDO), a Slave Select pin and an interrupt pin.
+///
+/// Note also that on the RFM22B (but not the RFM23B), it is required to control the TX_ANT and
+/// RX_ANT pins of the RFM22 in order to control the antenna connection properly. The RH_RF22
+/// driver is configured by default so that GPIO0 and GPIO1 outputs can
+/// control TX_ANT and RX_ANT input pins respectively automatically. On RFM22,
+/// you must connect GPIO0
+/// to TX_ANT and GPIO1 to RX_ANT for this automatic antenna switching to
+/// occur. See setGpioReversed() for more details. These connections are not required on RFM23B.
+///
+/// If you are using the Sparkfun RF22 shield, it will work with any 5V arduino without modification.
+/// Connect the RFM-22 module to most Arduino's like this (Caution, Arduino Mega has different pins for SPI,
+/// see below).
+/// \code
+/// Arduino RFM-22B
+/// GND----------GND-\ (ground in)
+/// SDN-/ (shutdown in)
+/// 3V3----------VCC (3.3V in)
+/// interrupt 0 pin D2-----------NIRQ (interrupt request out)
+/// SS pin D10----------NSEL (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT)
+/// \--TX_ANT (TX antenna control in) RFM22B only
+/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT)
+/// \--RX_ANT (RX antenna control in) RFM22B only
+/// \endcode
+/// For an Arduino Mega:
+/// \code
+/// Mega RFM-22B
+/// GND----------GND-\ (ground in)
+/// SDN-/ (shutdown in)
+/// 3V3----------VCC (3.3V in)
+/// interrupt 0 pin D2-----------NIRQ (interrupt request out)
+/// SS pin D53----------NSEL (chip select in)
+/// SCK pin D52----------SCK (SPI clock in)
+/// MOSI pin D51----------SDI (SPI Data in)
+/// MISO pin D50----------SDO (SPI data out)
+/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT)
+/// \--TX_ANT (TX antenna control in) RFM22B only
+/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT)
+/// \--RX_ANT (RX antenna control in) RFM22B only
+/// \endcode
+/// For Chipkit Uno32. Caution: you must also ensure jumper JP4 on the Uno32 is set to RD4
+/// \code
+/// Arduino RFM-22B
+/// GND----------GND-\ (ground in)
+/// SDN-/ (shutdown in)
+/// 3V3----------VCC (3.3V in)
+/// interrupt 0 pin D38----------NIRQ (interrupt request out)
+/// SS pin D10----------NSEL (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT)
+/// \--TX_ANT (TX antenna control in) RFM22B only
+/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT)
+/// \--RX_ANT (RX antenna control in) RFM22B only
+/// \endcode
+/// For Teensy 3.1
+/// \code
+/// Teensy RFM-22B
+/// GND----------GND-\ (ground in)
+/// SDN-/ (shutdown in)
+/// 3V3----------VCC (3.3V in)
+/// interrupt 2 pin D2-----------NIRQ (interrupt request out)
+/// SS pin D10----------NSEL (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT)
+/// \--TX_ANT (TX antenna control in) RFM22B only
+/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT)
+/// \--RX_ANT (RX antenna control in) RFM22B only
+/// \endcode
+/// For connecting an Arduino to an RFM23BP module. Note that the antenna control pins are reversed
+/// compared to the RF22.
+/// \code
+/// Arduino RFM-23BP
+/// GND----------GND-\ (ground in)
+/// SDN-/ (shutdown in)
+/// 5V-----------VCC (5V in)
+/// interrupt 0 pin D2-----------NIRQ (interrupt request out)
+/// SS pin D10----------NSEL (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// /--GPIO0 (GPIO0 out to control receiver antenna RXON)
+/// \--RXON (RX antenna control in)
+/// /--GPIO1 (GPIO1 out to control transmitter antenna TXON)
+/// \--TXON (TX antenna control in)
+/// \endcode
+///
+/// and you can then use the default constructor RH_RF22().
+/// You can override the default settings for the SS pin and the interrupt
+/// in the RH_RF22 constructor if you wish to connect the slave select SS to other than the normal one for your
+/// Arduino (D10 for Diecimila, Uno etc and D53 for Mega)
+/// or the interrupt request to other than pin D2 (Caution, different processors have different constraints as to the
+/// pins available for interrupts).
+///
+/// It is possible to have 2 radios connected to one Arduino, provided each radio has its own
+/// SS and interrupt line (SCK, SDI and SDO are common to both radios)
+///
+/// Caution: on some Arduinos such as the Mega 2560, if you set the slave select pin to be other than the usual SS
+/// pin (D53 on Mega 2560), you may need to set the usual SS pin to be an output to force the Arduino into SPI
+/// master mode.
+///
+/// Caution: Power supply requirements of the RF22 module may be relevant in some circumstances:
+/// RF22 modules are capable of pulling 80mA+ at full power, where Arduino's 3.3V line can
+/// give 50mA. You may need to make provision for alternate power supply for
+/// the RF22, especially if you wish to use full transmit power, and/or you have
+/// other shields demanding power. Inadequate power for the RF22 is reported to cause symptoms such as:
+/// - reset's/bootups terminate with "init failed" messages
+/// -random termination of communication after 5-30 packets sent/received
+/// -"fake ok" state, where initialization passes fluently, but communication doesn't happen
+/// -shields hang Arduino boards, especially during the flashing
+///
+/// Caution: some RF22 breakout boards (such as the HAB-RFM22B-BOA HAB-RFM22B-BO) reportedly
+/// have the TX_ANT and RX_ANT pre-connected to GPIO0 and GPIO1 round the wrong way. You can work with this
+/// if you use setGpioReversed().
+///
+/// Caution: If you are using a bare RF22 module without IO level shifters, you may have difficulty connecting
+/// to a 5V arduino. The RF22 module is 3.3V and its IO pins are 3.3V not 5V. Some Arduinos (Diecimila and
+/// Uno) seem to work OK with this, and some (Mega) do not always work reliably. Your Mileage May Vary.
+/// For best result, use level shifters, or use a RF22 shield or board with level shifters built in,
+/// such as the Sparkfun RFM22 shield http://www.sparkfun.com/products/11018.
+/// You could also use a 3.3V IO Arduino such as a Pro.
+/// It is recognised that it is difficult to connect
+/// the Sparkfun RFM22 shield to a Mega, since the SPI pins on the Mega are different to other Arduinos,
+/// But it is possible, by bending the SPI pins (D10, D11, D12, D13) on the
+/// shield out of the way before plugging it in to the Mega and jumpering the shield pins to the Mega like this:
+/// \code
+/// RF22 Shield Mega
+/// D10 D53
+/// D13 D52
+/// D11 D51
+/// D12 D50
+/// \endcode
+///
+/// \par Interrupts
+///
+/// The Driver uses interrupts to react to events in the RF22 module,
+/// such as the reception of a new packet, or the completion of transmission of a packet.
+/// The RH_RF22 interrupt service routine reads status from and writes data
+/// to the the RF22 module via the SPI interface. It is very important therefore,
+/// that if you are using the RF22 library with another SPI based deviced, that you
+/// disable interrupts while you transfer data to and from that other device.
+/// Use cli() to disable interrupts and sei() to reenable them.
+///
+/// \par SPI Interface
+///
+/// The RF22 module uses the SPI bus to communicate with the Arduino. Arduino
+/// IDE includes a hardware SPI class to communicate with SPI devices using
+/// the SPI facilities built into the Atmel chips, over the standard designated
+/// SPI pins MOSI, MISO, SCK, which are usually on Arduino pins 11, 12 and 13
+/// respectively (or 51, 50, 52 on a Mega).
+///
+/// By default, the RH_RF22 Driver uses the Hardware SPI interface to
+/// communicate with the RF22 module. However, if your RF22 SPI is connected to
+/// the Arduino through non-standard pins, or the standard Hardware SPI
+/// interface will not work for you, you can instead use a bit-banged Software
+/// SPI class RHSoftwareSPI, which can be configured to work on any Arduino digital IO pins.
+/// See the documentation of RHSoftwareSPI for details.
+///
+/// The advantages of the Software SPI interface are that it can be used on
+/// any Arduino pins, not just the usual dedicated hardware pins. The
+/// disadvantage is that it is significantly slower then hardware.
+/// If you observe reliable behaviour with the default hardware SPI RHHardwareSPI, but unreliable behaviour
+/// with Software SPI RHSoftwareSPI, it may be due to slow CPU performance.
+///
+/// Initialisation example with hardware SPI
+/// \code
+/// #include <RH_RF22.h>
+/// RH_RF22 driver;
+/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
+/// \endcode
+///
+/// Initialisation example with software SPI
+/// \code
+/// #include <RH_RF22.h>
+/// #include <RHSoftwareSPI.h>
+/// RHSoftwareSPI spi;
+/// RH_RF22 driver(10, 2, spi);
+/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
+/// \endcode
+///
+/// \par Memory
+///
+/// The RH_RF22 Driver requires non-trivial amounts of memory. The sample programs all compile to
+/// about 9 to 14kbytes each on Arduino, which will fit in the flash proram memory of most Arduinos. However,
+/// the RAM requirements are more critical. Most sample programs above will run on Duemilanova,
+/// but not on Diecimila. Even on Duemilanova, the RAM requirements are very close to the
+/// available memory of 2kbytes. Therefore, you should be vary sparing with RAM use in programs that use
+/// the RH_RF22 Driver on Duemilanova.
+///
+/// The sample RHRouter and RHMesh programs compile to about 14kbytes,
+/// and require more RAM than the others.
+/// They will not run on Duemilanova or Diecimila, but will run on Arduino Mega.
+///
+/// It is often hard to accurately identify when you are hitting RAM limits on Arduino.
+/// The symptoms can include:
+/// - Mysterious crashes and restarts
+/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements)
+/// - Hanging
+/// - Output from Serial.print() not appearing
+///
+/// With an Arduino Mega, with 8 kbytes of SRAM, there is much more RAM headroom for
+/// your own elaborate programs.
+/// This library is reported to work with Arduino Pro Mini, but that has not been tested by me.
+///
+/// The RF22M modules use an inexpensive crystal to control the frequency synthesizer, and therfore you can expect
+/// the transmitter and receiver frequencies to be subject to the usual inaccuracies of such crystals. The RF22
+/// contains an AFC circuit to compensate for differences in transmitter and receiver frequencies.
+/// It does this by altering the receiver frequency during reception by up to the pull-in frequency range.
+/// This RF22 library enables the AFC and by default sets the pull-in frequency range to
+/// 0.05MHz, which should be sufficient to handle most situations. However, if you observe unexplained packet losses
+/// or failure to operate correctly all the time it may be because your modules have a wider frequency difference, and
+/// you may need to set the afcPullInRange to a different value, using setFrequency();
+///
+/// \par Transmitter Power
+///
+/// You can control the transmitter power on the RF22 and RF23 transceivers
+/// with the RH_RF22::setTxPower() function. The argument can be any of the
+/// RH_RF22_TXPOW_* (for RFM22) or RH_RF22_RF23B_TXPOW_* (for RFM23) values.
+/// The default is RH_RF22_TXPOW_8DBM/RH_RF22_RF23B_TXPOW_1DBM . Eg:
+/// \code
+/// driver.setTxPower(RH_RF22_TXPOW_2DBM);
+/// \endcode
+///
+/// The RF23BP has higher power capability, there are
+/// several power settings that are specific to the RF23BP only:
+///
+/// - RH_RF22_RF23BP_TXPOW_28DBM
+/// - RH_RF22_RF23BP_TXPOW_29DBM
+/// - RH_RF22_RF23BP_TXPOW_38DBM
+///
+/// CAUTION: the high power settings available on the RFM23BP require
+/// significant power supply current. For example at +30dBm, the typical chip
+/// supply current is 550mA. This will overwhelm some small CPU board power
+/// regulators and USB supplies. If you use this chip at high power make sure
+/// you have an adequate supply current providing full 5V to the RFM23BP (and
+/// the CPU if required), otherwise you can expect strange behaviour like
+/// hanging, stopping, incorrect power levels, RF power amp overheating etc.
+/// You must also ensure that the RFM23BP GPIO pins are connected to the
+/// antenna switch control pins like so:
+////
+/// \code
+/// GPIO0 <-> RXON
+/// GPIO1 <-> TXON
+/// \endcode
+///
+/// The RF output impedance of the RFM22BP module is 50 ohms. In our
+/// experiments we found that the most critical issue (besides a suitable
+/// power supply) is to ensure that the antenna impedance is also near 50
+/// ohms. Connecting a simple 1/4 wavelength (ie a 17.3cm single wire)
+/// directly to the antenna output <b>will not work at full 30dBm power</b>,
+/// and will result in the transmitter hanging and/or the power amp
+/// overheating. Connect a proper 50 ohm impedance transmission line or
+/// antenna, and prevent RF radiation into the radio and arduino modules,
+/// in order to get full, reliable power. Our tests show that a 433MHz
+/// RFM23BP feeding a 50 ohm transmission line with a VHF discone antenna at
+/// the end results in full power output and the power amp transistor on the
+/// RFM22BP module runnning slightly warm but not hot. We recommend you use
+/// the services of a competent RF engineer when trying to use this high power
+/// module.
+///
+/// Note: with RFM23BP, the reported maximum possible power when operating on 3.3V is 27dBm.
+///
+/// We have made some actual power measurements against
+/// programmed power for Sparkfun RFM22 wireless module under the following conditions:
+/// - Sparkfun RFM22 wireless module, Duemilanove, USB power
+/// - 10cm RG58C/U soldered direct to RFM22 module ANT and GND
+/// - bnc connecteor
+/// - 12dB attenuator
+/// - BNC-SMA adapter
+/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
+/// - Tektronix TDS220 scope to measure the Vout from power head
+/// \code
+/// Program power Measured Power
+/// dBm dBm
+/// 1 -5.6
+/// 2 -3.8
+/// 5 -2.2
+/// 8 -0.6
+/// 11 1.2
+/// 14 11.6
+/// 17 14.4
+/// 20 18.0
+/// \endcode
+/// (Caution: we dont claim laboratory accuracy for these measurements)
+/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna.
+///
+/// \par Performance
+///
+/// Some simple speed performance tests have been conducted.
+/// In general packet transmission rate will be limited by the modulation scheme.
+/// Also, if your code does any slow operations like Serial printing it will also limit performance.
+/// We disabled any printing in the tests below.
+/// We tested with RH_RF22::GFSK_Rb125Fd125, which is probably the fastest scheme available.
+/// We tested with a 13 octet message length, over a very short distance of 10cm.
+///
+/// Transmission (no reply) tests with modulation RH_RF22::GFSK_Rb125Fd125 and a
+/// 13 octet message show about 330 messages per second transmitted.
+///
+/// Transmit-and-wait-for-a-reply tests with modulation RH_RF22::GFSK_Rb125Fd125 and a
+/// 13 octet message (send and receive) show about 160 round trips per second.
+///
+/// \par Compatibility with RF22 library
+/// The RH_RF22 driver is based on our earlier RF22 library http://www.airspayce.com/mikem/arduino/RF22
+/// We have tried hard to be as compatible as possible with the earlier RF22 library, but there are some differences:
+/// - Different constructor.
+/// - Indexes for some modem configurations have changed (we recommend you use the symbolic names, not integer indexes).
+///
+/// The major difference is that under RadioHead, you are
+/// required to create 2 objects (ie RH_RF22 and a manager) instead of just one object under RF22
+/// (ie RHMesh, RHRouter, RHReliableDatagram or RHDatagram).
+/// It may be sufficient or you to change for example:
+/// \code
+/// RF22ReliableDatagram rf22(CLIENT_ADDRESS);
+/// \endcode
+/// to:
+/// \code
+/// RH_RF22 driver;
+/// RHReliableDatagram rf22(driver, CLIENT_ADDRESS);
+/// \endcode
+/// and any instance of RF22_MAX_MESSAGE_LEN to RH_RF22_MAX_MESSAGE_LEN
+///
+/// RadioHead version 1.6 changed the way the interrupt pin number is
+/// specified on Arduino and Uno32 platforms. If your code previously
+/// specifed a non-default interrupt pin number in the RH_RF22 constructor,
+/// you may need to review your code to specify the correct interrrupt pin
+/// (and not the interrupt number as before).
+class RH_RF22 : public RHSPIDriver
+{
+public:
+
+ /// \brief Defines register values for a set of modem configuration registers
+ ///
+ /// Defines register values for a set of modem configuration registers
+ /// that can be passed to setModemConfig()
+ /// if none of the choices in ModemConfigChoice suit your need
+ /// setModemConfig() writes the register values to the appropriate RH_RF22 registers
+ /// to set the desired modulation type, data rate and deviation/bandwidth.
+ /// Suitable values for these registers can be computed using the register calculator at
+ /// http://www.hoperf.com/upload/rf/RF22B%2023B%2031B%2042B%2043B%20Register%20Settings_RevB1-v5.xls
+ typedef struct
+ {
+ uint8_t reg_1c; ///< Value for register RH_RF22_REG_1C_IF_FILTER_BANDWIDTH
+ uint8_t reg_1f; ///< Value for register RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE
+ uint8_t reg_20; ///< Value for register RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE
+ uint8_t reg_21; ///< Value for register RH_RF22_REG_21_CLOCK_RECOVERY_OFFSET2
+ uint8_t reg_22; ///< Value for register RH_RF22_REG_22_CLOCK_RECOVERY_OFFSET1
+ uint8_t reg_23; ///< Value for register RH_RF22_REG_23_CLOCK_RECOVERY_OFFSET0
+ uint8_t reg_24; ///< Value for register RH_RF22_REG_24_CLOCK_RECOVERY_TIMING_LOOP_GAIN1
+ uint8_t reg_25; ///< Value for register RH_RF22_REG_25_CLOCK_RECOVERY_TIMING_LOOP_GAIN0
+ uint8_t reg_2c; ///< Value for register RH_RF22_REG_2C_OOK_COUNTER_VALUE_1
+ uint8_t reg_2d; ///< Value for register RH_RF22_REG_2D_OOK_COUNTER_VALUE_2
+ uint8_t reg_2e; ///< Value for register RH_RF22_REG_2E_SLICER_PEAK_HOLD
+ uint8_t reg_58; ///< Value for register RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING
+ uint8_t reg_69; ///< Value for register RH_RF22_REG_69_AGC_OVERRIDE1
+ uint8_t reg_6e; ///< Value for register RH_RF22_REG_6E_TX_DATA_RATE1
+ uint8_t reg_6f; ///< Value for register RH_RF22_REG_6F_TX_DATA_RATE0
+ uint8_t reg_70; ///< Value for register RH_RF22_REG_70_MODULATION_CONTROL1
+ uint8_t reg_71; ///< Value for register RH_RF22_REG_71_MODULATION_CONTROL2
+ uint8_t reg_72; ///< Value for register RH_RF22_REG_72_FREQUENCY_DEVIATION
+ } ModemConfig;
+
+ /// Choices for setModemConfig() for a selected subset of common modulation types,
+ /// and data rates. If you need another configuration, use the register calculator.
+ /// and call setModemRegisters() with your desired settings.
+ /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic
+ /// definitions and not their integer equivalents: its possible that new values will be
+ /// introduced in later versions (though we will try to avoid it).
+ typedef enum
+ {
+ UnmodulatedCarrier = 0, ///< Unmodulated carrier for testing
+ FSK_PN9_Rb2Fd5, ///< FSK, No Manchester, Rb = 2kbs, Fd = 5kHz, PN9 random modulation for testing
+
+ FSK_Rb2Fd5, ///< FSK, No Manchester, Rb = 2kbs, Fd = 5kHz
+ FSK_Rb2_4Fd36, ///< FSK, No Manchester, Rb = 2.4kbs, Fd = 36kHz
+ FSK_Rb4_8Fd45, ///< FSK, No Manchester, Rb = 4.8kbs, Fd = 45kHz
+ FSK_Rb9_6Fd45, ///< FSK, No Manchester, Rb = 9.6kbs, Fd = 45kHz
+ FSK_Rb19_2Fd9_6, ///< FSK, No Manchester, Rb = 19.2kbs, Fd = 9.6kHz
+ FSK_Rb38_4Fd19_6, ///< FSK, No Manchester, Rb = 38.4kbs, Fd = 19.6kHz
+ FSK_Rb57_6Fd28_8, ///< FSK, No Manchester, Rb = 57.6kbs, Fd = 28.8kHz
+ FSK_Rb125Fd125, ///< FSK, No Manchester, Rb = 125kbs, Fd = 125kHz
+ FSK_Rb_512Fd2_5, ///< FSK, No Manchester, Rb = 512bs, Fd = 2.5kHz, for POCSAG compatibility
+ FSK_Rb_512Fd4_5, ///< FSK, No Manchester, Rb = 512bs, Fd = 4.5kHz, for POCSAG compatibility
+
+ GFSK_Rb2Fd5, ///< GFSK, No Manchester, Rb = 2kbs, Fd = 5kHz
+ GFSK_Rb2_4Fd36, ///< GFSK, No Manchester, Rb = 2.4kbs, Fd = 36kHz
+ GFSK_Rb4_8Fd45, ///< GFSK, No Manchester, Rb = 4.8kbs, Fd = 45kHz
+ GFSK_Rb9_6Fd45, ///< GFSK, No Manchester, Rb = 9.6kbs, Fd = 45kHz
+ GFSK_Rb19_2Fd9_6, ///< GFSK, No Manchester, Rb = 19.2kbs, Fd = 9.6kHz
+ GFSK_Rb38_4Fd19_6, ///< GFSK, No Manchester, Rb = 38.4kbs, Fd = 19.6kHz
+ GFSK_Rb57_6Fd28_8, ///< GFSK, No Manchester, Rb = 57.6kbs, Fd = 28.8kHz
+ GFSK_Rb125Fd125, ///< GFSK, No Manchester, Rb = 125kbs, Fd = 125kHz
+
+ OOK_Rb1_2Bw75, ///< OOK, No Manchester, Rb = 1.2kbs, Rx Bandwidth = 75kHz
+ OOK_Rb2_4Bw335, ///< OOK, No Manchester, Rb = 2.4kbs, Rx Bandwidth = 335kHz
+ OOK_Rb4_8Bw335, ///< OOK, No Manchester, Rb = 4.8kbs, Rx Bandwidth = 335kHz
+ OOK_Rb9_6Bw335, ///< OOK, No Manchester, Rb = 9.6kbs, Rx Bandwidth = 335kHz
+ OOK_Rb19_2Bw335, ///< OOK, No Manchester, Rb = 19.2kbs, Rx Bandwidth = 335kHz
+ OOK_Rb38_4Bw335, ///< OOK, No Manchester, Rb = 38.4kbs, Rx Bandwidth = 335kHz
+ OOK_Rb40Bw335 ///< OOK, No Manchester, Rb = 40kbs, Rx Bandwidth = 335kHz
+
+ } ModemConfigChoice;
+
+ /// \brief Defines the available choices for CRC
+ /// Types of permitted CRC polynomials, to be passed to setCRCPolynomial()
+ /// They deliberately have the same numeric values as the crc[1:0] field of Register
+ /// RH_RF22_REG_30_DATA_ACCESS_CONTROL
+ typedef enum
+ {
+ CRC_CCITT = 0, ///< CCITT
+ CRC_16_IBM = 1, ///< CRC-16 (IBM) The default used by RH_RF22 driver
+ CRC_IEC_16 = 2, ///< IEC-16
+ CRC_Biacheva = 3 ///< Biacheva
+ } CRCPolynomial;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface
+ /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient
+ /// distinct interrupt lines, one for each instance.
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RH_RF22 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
+ /// \param[in] interruptPin The interrupt Pin number that is connected to the RF22 NIRQ interrupt line.
+ /// Defaults to pin 2, as required by sparkfun RFM22 module shields.
+ /// Caution: You must specify an interrupt capable pin.
+ /// On many Arduino boards, there are limitations as to which pins may be used as interrupts.
+ /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin.
+ /// On other Arduinos pins 2 or 3.
+ /// See http://arduino.cc/en/Reference/attachInterrupt for more details.
+ /// On Chipkit Uno32, pins 38, 2, 7, 8, 35.
+ /// On other boards, any digital pin may be used.
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_RF22(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:
+ /// - Initialise the slave select pin and the SPI interface library
+ /// - Software reset the RH_RF22 module
+ /// - Checks the connected RH_RF22 module is either a RH_RF22_DEVICE_TYPE_RX_TRX or a RH_RF22_DEVICE_TYPE_TX
+ /// - Attaches an interrupt handler
+ /// - Configures the RH_RF22 module
+ /// - Sets the frequency to 434.0 MHz
+ /// - Sets the modem data rate to FSK_Rb2_4Fd36
+ /// \return true if everything was successful
+ bool init();
+
+ /// Issues a software reset to the
+ /// RH_RF22 module. Blocks for 1ms to ensure the reset is complete.
+ void reset();
+
+ /// Reads and returns the device status register RH_RF22_REG_02_DEVICE_STATUS
+ /// \return The value of the device status register
+ uint8_t statusRead();
+
+ /// Reads a value from the on-chip analog-digital converter
+ /// \param[in] adcsel Selects the ADC input to measure. One of RH_RF22_ADCSEL_*. Defaults to the
+ /// internal temperature sensor
+ /// \param[in] adcref Specifies the refernce voltage to use. One of RH_RF22_ADCREF_*.
+ /// Defaults to the internal bandgap voltage.
+ /// \param[in] adcgain Amplifier gain selection.
+ /// \param[in] adcoffs Amplifier offseet (0 to 15).
+ /// \return The analog value. 0 to 255.
+ uint8_t adcRead(uint8_t adcsel = RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR,
+ uint8_t adcref = RH_RF22_ADCREF_BANDGAP_VOLTAGE,
+ uint8_t adcgain = 0,
+ uint8_t adcoffs = 0);
+
+ /// Reads the on-chip temperature sensor
+ /// \param[in] tsrange Specifies the temperature range to use. One of RH_RF22_TSRANGE_*
+ /// \param[in] tvoffs Specifies the temperature value offset. This is actually signed value
+ /// added to the measured temperature value
+ /// \return The measured temperature.
+ uint8_t temperatureRead(uint8_t tsrange = RH_RF22_TSRANGE_M64_64C, uint8_t tvoffs = 0);
+
+ /// Reads the wakeup timer value in registers RH_RF22_REG_17_WAKEUP_TIMER_VALUE1
+ /// and RH_RF22_REG_18_WAKEUP_TIMER_VALUE2
+ /// \return The wakeup timer value
+ uint16_t wutRead();
+
+ /// Sets the wakeup timer period registers RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1,
+ /// RH_RF22_REG_15_WAKEUP_TIMER_PERIOD2 and RH_RF22_R<EG_16_WAKEUP_TIMER_PERIOD3
+ /// \param[in] wtm Wakeup timer mantissa value
+ /// \param[in] wtr Wakeup timer exponent R value
+ /// \param[in] wtd Wakeup timer exponent D value
+ void setWutPeriod(uint16_t wtm, uint8_t wtr = 0, uint8_t wtd = 0);
+
+ /// Sets the transmitter and receiver centre frequency
+ /// \param[in] centre Frequency in MHz. 240.0 to 960.0. Caution, some versions of RH_RF22 and derivatives
+ /// implemented more restricted frequency ranges.
+ /// \param[in] afcPullInRange Sets the AF Pull In Range in MHz. Defaults to 0.05MHz (50kHz).
+ /// Range is 0.0 to 0.159375
+ /// for frequencies 240.0 to 480MHz, and 0.0 to 0.318750MHz for frequencies 480.0 to 960MHz,
+ /// \return true if the selected frquency centre + (fhch * fhs) is within range and the afcPullInRange
+ /// is within range
+ bool setFrequency(float centre, float afcPullInRange = 0.05);
+
+ /// Sets the frequency hopping step size.
+ /// \param[in] fhs Frequency Hopping step size in 10kHz increments
+ /// \return true if centre + (fhch * fhs) is within limits
+ bool setFHStepSize(uint8_t fhs);
+
+ /// Sets the frequncy hopping channel. Adds fhch * fhs to centre frequency
+ /// \param[in] fhch The channel number
+ /// \return true if the selected frquency centre + (fhch * fhs) is within range
+ bool setFHChannel(uint8_t fhch);
+
+ /// Reads and returns the current RSSI value from register RH_RF22_REG_26_RSSI. Caution: this is
+ /// in internal units (see figure 31 of RFM22B/23B documentation), not in dBm. If you want to find the RSSI in dBm
+ /// of the last received message, use lastRssi() instead.
+ /// \return The current RSSI value
+ uint8_t rssiRead();
+
+ /// Reads and returns the current EZMAC value from register RH_RF22_REG_31_EZMAC_STATUS
+ /// \return The current EZMAC value
+ uint8_t ezmacStatusRead();
+
+ /// Sets the parameters for the RH_RF22 Idle mode in register RH_RF22_REG_07_OPERATING_MODE.
+ /// Idle mode is the mode the RH_RF22 will be in when not transmitting or receiving. The default idle mode
+ /// is RH_RF22_XTON ie READY mode.
+ /// \param[in] mode Mask of mode bits, using RH_RF22_SWRES, RH_RF22_ENLBD, RH_RF22_ENWT,
+ /// RH_RF22_X32KSEL, RH_RF22_PLLON, RH_RF22_XTON.
+ void setOpMode(uint8_t mode);
+
+ /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running,
+ /// disables them.
+ void setModeIdle();
+
+ /// If current mode is Tx or Idle, changes it to Rx.
+ /// Starts the receiver in the RH_RF22.
+ void setModeRx();
+
+ /// If current mode is Rx or Idle, changes it to Rx.
+ /// Starts the transmitter in the RH_RF22.
+ void setModeTx();
+
+ /// Sets the transmitter power output level in register RH_RF22_REG_6D_TX_POWER.
+ /// Be a good neighbour and set the lowest power level you need.
+ /// After init(), the power will be set to RH_RF22::RH_RF22_TXPOW_8DBM on RF22B
+ /// or RH_RF22_RF23B_TXPOW_1DBM on an RF23B.
+ /// The highest power available on RF22B is RH_RF22::RH_RF22_TXPOW_20DBM (20dBm).
+ /// The highest power available on RF23B is RH_RF22::RH_RF22_RF23B_TXPOW_13DBM (13dBm).
+ /// Higher powers are available on RF23BP (using RH_RF22_RF23BP_TXPOW_*),
+ /// and then only with an adequate power supply. See comments above.
+ /// Caution: In some countries you may only select certain higher power levels if you
+ /// are also using frequency hopping. Make sure you are aware of the legal
+ /// limitations and regulations in your region.
+ /// \param[in] power Transmitter power level, one of RH_RF22_*TXPOW_*
+ void setTxPower(uint8_t power);
+
+ /// Sets all the registered required to configure the data modem in the RH_RF22, including the data rate,
+ /// bandwidths etc. You cas use this to configure the modem with custom configuraitons if none of the
+ /// canned configurations in ModemConfigChoice suit you.
+ /// \param[in] config A ModemConfig structure containing values for the modem configuration registers.
+ void setModemRegisters(const ModemConfig* config);
+
+ /// Select one of the predefined modem configurations. If you need a modem configuration not provided
+ /// here, use setModemRegisters() with your own ModemConfig.
+ /// \param[in] index The configuration choice.
+ /// \return true if index is a valid choice.
+ bool setModemConfig(ModemConfigChoice index);
+
+ /// Starts the receiver and checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is NOT permitted.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// Sets the length of the preamble
+ /// in 4-bit nibbles.
+ /// Caution: this should be set to the same
+ /// value on all nodes in your network. Default is 8.
+ /// Sets the message preamble length in RH_RF22_REG_34_PREAMBLE_LENGTH
+ /// \param[in] nibbles Preamble length in nibbles of 4 bits each.
+ void setPreambleLength(uint8_t nibbles);
+
+ /// Sets the sync words for transmit and receive in registers RH_RF22_REG_36_SYNC_WORD3
+ /// to RH_RF22_REG_39_SYNC_WORD0
+ /// Caution: SyncWords should be set to the same
+ /// value on all nodes in your network. Nodes with different SyncWords set will never receive
+ /// each others messages, so different SyncWords can be used to isolate different
+ /// networks from each other. Default is { 0x2d, 0xd4 }.
+ /// \param[in] syncWords Array of sync words, 1 to 4 octets long
+ /// \param[in] len Number of sync words to set, 1 to 4.
+ void setSyncWords(const uint8_t* syncWords, uint8_t len);
+
+ /// Tells the receiver to accept messages with any TO address, not just messages
+ /// addressed to thisAddress or the broadcast address
+ /// \param[in] promiscuous true if you wish to receive messages with any TO address
+ virtual void setPromiscuous(bool promiscuous);
+
+ /// Sets the CRC polynomial to be used to generate the CRC for both receive and transmit
+ /// otherwise the default of CRC_16_IBM will be used.
+ /// \param[in] polynomial One of RH_RF22::CRCPolynomial choices CRC_*
+ /// \return true if polynomial is a valid option for this radio.
+ bool setCRCPolynomial(CRCPolynomial polynomial);
+
+ /// Configures GPIO pins for reversed GPIO connections to the antenna switch.
+ /// Normally on RF22 modules, GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit
+ /// and GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive. The RH_RF22 driver
+ /// configures the GPIO pins during init() so the antenna switch works as expected.
+ /// However, some RF22 modules, such as HAB-RFM22B-BOA HAB-RFM22B-BO, also Si4432 sold by Dorji.com via Tindie.com
+ /// have these GPIO pins reversed, so that GPIO0 is connected to RX_ANT.
+ /// Call this function with a true argument after init() and before transmitting
+ /// in order to configure the module for reversed GPIO pins.
+ /// \param[in] gpioReversed Set to true if your RF22 module has reversed GPIO antenna switch connections.
+ void setGpioReversed(bool gpioReversed = false);
+
+ /// Returns the time in millis since the last preamble was received, and when the last
+ /// RSSI measurement was made.
+ uint32_t getLastPreambleTime();
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+ /// Sets the radio into low-power sleep mode.
+ /// If successful, the transport will stay in sleep mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
+ /// \return true if sleep mode was successfully entered.
+ virtual bool sleep();
+
+protected:
+ /// This is a low level function to handle the interrupts for one instance of RH_RF22.
+ /// Called automatically by isr*()
+ /// Should not need to be called.
+ void handleInterrupt();
+
+ /// Clears the receiver buffer.
+ /// Internal use only
+ void clearRxBuf();
+
+ /// Clears the transmitter buffer
+ /// Internal use only
+ void clearTxBuf();
+
+ /// Fills the transmitter buffer with the data of a mesage to be sent
+ /// \param[in] data Array of data bytes to be sent (1 to 255)
+ /// \param[in] len Number of data bytes in data (> 0)
+ /// \return true if the message length is valid
+ bool fillTxBuf(const uint8_t* data, uint8_t len);
+
+ /// Appends the transmitter buffer with the data of a mesage to be sent
+ /// \param[in] data Array of data bytes to be sent (0 to 255)
+ /// \param[in] len Number of data bytes in data
+ /// \return false if the resulting message would exceed RH_RF22_MAX_MESSAGE_LEN, else true
+ bool appendTxBuf(const uint8_t* data, uint8_t len);
+
+ /// Internal function to load the next fragment of
+ /// the current message into the transmitter FIFO
+ /// Internal use only
+ void sendNextFragment();
+
+ /// function to copy the next fragment from
+ /// the receiver FIF) into the receiver buffer
+ void readNextFragment();
+
+ /// Clears the RF22 Rx and Tx FIFOs
+ /// Internal use only
+ void resetFifos();
+
+ /// Clears the RF22 Rx FIFO
+ /// Internal use only
+ void resetRxFifo();
+
+ /// Clears the RF22 Tx FIFO
+ /// Internal use only
+ void resetTxFifo();
+
+ /// This function will be called by handleInterrupt() if an RF22 external interrupt occurs.
+ /// This can only happen if external interrupts are enabled in the RF22
+ /// (which they are not by default).
+ /// Subclasses may override this function to get control when an RF22 external interrupt occurs.
+ virtual void handleExternalInterrupt();
+
+ /// This function will be called by handleInterrupt() if an RF22 wakeup timer interrupt occurs.
+ /// This can only happen if wakeup timer interrupts are enabled in theRF22
+ /// (which they are not by default).
+ /// Subclasses may override this function to get control when an RF22 wakeup timer interrupt occurs.
+ virtual void handleWakeupTimerInterrupt();
+
+ /// Start the transmission of the contents
+ /// of the Tx buffer
+ void startTransmit();
+
+ /// ReStart the transmission of the contents
+ /// of the Tx buffer after a atransmission failure
+ void restartTransmit();
+
+ void setThisAddress(uint8_t thisAddress);
+
+ /// Sets the radio operating mode for the case when the driver is idle (ie not
+ /// transmitting or receiving), allowing you to control the idle mode power requirements
+ /// at the expense of slower transitions to transmit and receive modes.
+ /// By default, the idle mode is RH_RF22_XTON,
+ /// but eg setIdleMode(RH_RF22_PLL) will provide a much lower
+ /// idle current but slower transitions. Call this function after init().
+ /// \param[in] idleMode The chip operating mode to use when the driver is idle. One of the valid definitions for RH_RF22_REG_07_OPERATING_MODE
+ void setIdleMode(uint8_t idleMode);
+
+protected:
+ /// Low level interrupt service routine for RF22 connected to interrupt 0
+ static void isr0();
+
+ /// Low level interrupt service routine for RF22 connected to interrupt 1
+ static void isr1();
+
+ /// Low level interrupt service routine for RF22 connected to interrupt 1
+ static void isr2();
+
+ /// Array of instances connected to interrupts 0 and 1
+ static RH_RF22* _deviceForInterrupt[];
+
+ /// Index of next interrupt number to use in _deviceForInterrupt
+ static uint8_t _interruptCount;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ /// The configured interrupt pin connected to this instance
+ InterruptIn _interruptPin;
+#else
+ /// The configured interrupt pin connected to this instance
+ uint8_t _interruptPin;
+#endif
+
+ /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
+ /// else 0xff
+ uint8_t _myInterruptIndex;
+
+ /// The radio mode to use when mode is idle
+ uint8_t _idleMode;
+
+ /// The device type reported by the RF22
+ uint8_t _deviceType;
+
+ /// The selected CRC polynomial
+ CRCPolynomial _polynomial;
+
+ // These volatile members may get changed in the interrupt service routine
+ /// Number of octets in the receiver buffer
+ volatile uint8_t _bufLen;
+
+ /// The receiver buffer
+ uint8_t _buf[RH_RF22_MAX_MESSAGE_LEN];
+
+ /// True when there is a valid message in the Rx buffer
+ volatile bool _rxBufValid;
+
+ /// Index into TX buffer of the next to send chunk
+ volatile uint8_t _txBufSentIndex;
+
+ /// Time in millis since the last preamble was received (and the last time the RSSI was measured)
+ uint32_t _lastPreambleTime;
+};
+
+/// @example rf22_client.pde
+/// @example rf22_server.pde
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF24.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,1172 @@
+// RH_RF24.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF24.cpp,v 1.14 2015/08/13 02:45:47 mikem Exp $
+
+#include <RH_RF24.h>
+// Generated with Silicon Labs WDS software:
+#include "radio_config_Si4460.h"
+
+// Interrupt vectors for the 3 Arduino interrupt pins
+// Each interrupt can be handled by a different instance of RH_RF24, allowing you to have
+// 2 or more RF24s per Arduino
+RH_RF24* RH_RF24::_deviceForInterrupt[RH_RF24_NUM_INTERRUPTS] = {0, 0, 0};
+uint8_t RH_RF24::_interruptCount = 0; // Index into _deviceForInterrupt for next device
+
+// This configuration data is defined in radio_config_Si4460.h
+// which was generated with the Silicon Labs WDS program
+PROGMEM const uint8_t RFM26_CONFIGURATION_DATA[] = RADIO_CONFIGURATION_DATA_ARRAY;
+
+// These configurations were all generated originally by the Silicon LAbs WDS configuration tool.
+// The configurations were imported into RH_RF24, the complete properties set dumped to a file with printRegisters, then
+// RH_RF24_property_data/convert.pl was used to generate the entry for this table.
+// Contributions of new complete and tested ModemConfigs ready to add to this list will be readily accepted.
+// Casual suggestions of new schemes without working examples will probably be passed over
+PROGMEM static const RH_RF24::ModemConfig MODEM_CONFIG_TABLE[] =
+{
+ // These were generated with convert.pl from data in RH_RF24_property_data
+ // FSK_Rb0_5Fd1
+ { 0x02, 0x00, 0x13, 0x88, 0x01, 0x00, 0x00, 0x46, 0x01, 0x34, 0x11, 0x02, 0x71, 0x00, 0xd1, 0xb7, 0x00, 0x69, 0x02, 0x36, 0x80, 0x01, 0x5a, 0xfc, 0xe2, 0x11, 0x89, 0x89, 0x00, 0x02, 0xff, 0xff, 0x00, 0x2b, 0x02, 0x81, 0x00, 0xad, 0x3a, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // FSK_Rb5Fd10
+ { 0x02, 0x00, 0xc3, 0x50, 0x01, 0x00, 0x02, 0xbb, 0x01, 0x30, 0x20, 0x01, 0x77, 0x01, 0x5d, 0x86, 0x00, 0xaf, 0x02, 0x36, 0x80, 0x0f, 0x15, 0x87, 0xe2, 0x11, 0x52, 0x52, 0x00, 0x02, 0xff, 0xff, 0x00, 0x2a, 0x02, 0x83, 0x01, 0x20, 0x40, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // FSK_Rb50Fd100
+ { 0x02, 0x07, 0xa1, 0x20, 0x01, 0x00, 0x1b, 0x4f, 0x01, 0x00, 0x10, 0x00, 0xc8, 0x02, 0x8f, 0x5c, 0x01, 0x48, 0x02, 0x36, 0x80, 0x92, 0x0a, 0x46, 0xe2, 0x11, 0x2c, 0x2c, 0x00, 0x02, 0xff, 0xff, 0x00, 0x29, 0x02, 0x83, 0x02, 0x7f, 0x40, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ //FSK_Rb150Fd300
+ { 0x02, 0x16, 0xe3, 0x60, 0x01, 0x00, 0x51, 0xec, 0x01, 0x00, 0x30, 0x00, 0xc8, 0x02, 0x8f, 0x5c, 0x01, 0x48, 0x02, 0x47, 0x83, 0x6a, 0x04, 0xb5, 0xe2, 0x22, 0x16, 0x16, 0x00, 0x02, 0xff, 0xff, 0x00, 0x29, 0x02, 0x83, 0x02, 0x7f, 0x40, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0x3f, 0x39, 0x04, 0x05, 0x04, 0x01, },
+
+ // GFSK_Rb0_5Fd1
+ { 0x03, 0x00, 0x4e, 0x20, 0x05, 0x00, 0x00, 0x46, 0x01, 0x34, 0x11, 0x02, 0x71, 0x00, 0xd1, 0xb7, 0x00, 0x69, 0x02, 0x36, 0x80, 0x01, 0x5a, 0xfc, 0xe2, 0x11, 0x89, 0x89, 0x00, 0x1a, 0xff, 0xff, 0x00, 0x2b, 0x02, 0x81, 0x00, 0x68, 0x3a, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // GFSK_Rb5Fd10
+ { 0x03, 0x03, 0x0d, 0x40, 0x05, 0x00, 0x02, 0xbb, 0x01, 0x30, 0x20, 0x01, 0x77, 0x01, 0x5d, 0x86, 0x00, 0xaf, 0x02, 0x36, 0x80, 0x0f, 0x15, 0x87, 0xe2, 0x11, 0x52, 0x52, 0x00, 0x1a, 0xff, 0xff, 0x00, 0x2a, 0x02, 0x83, 0x00, 0xad, 0x40, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // GFSK_Rb50Fd100
+ { 0x03, 0x0f, 0x42, 0x40, 0x09, 0x00, 0x1b, 0x4f, 0x01, 0x00, 0x10, 0x00, 0xc8, 0x02, 0x8f, 0x5c, 0x01, 0x48, 0x02, 0x36, 0x80, 0x92, 0x0a, 0x46, 0xe2, 0x11, 0x2c, 0x2c, 0x00, 0x1a, 0xff, 0xff, 0x00, 0x29, 0x02, 0x83, 0x01, 0x7f, 0x40, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // GFSK_Rb150Fd300
+ { 0x03, 0x2d, 0xc6, 0xc0, 0x09, 0x00, 0x51, 0xec, 0x01, 0x00, 0x30, 0x00, 0xc8, 0x02, 0x8f, 0x5c, 0x01, 0x48, 0x02, 0x47, 0x83, 0x6a, 0x04, 0xb5, 0xe2, 0x22, 0x16, 0x16, 0x00, 0x1a, 0xff, 0xff, 0x00, 0x29, 0x02, 0x83, 0x01, 0x7f, 0x40, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0x3f, 0x39, 0x04, 0x05, 0x04, 0x01, },
+
+ // OOK_Rb5Bw30
+ { 0x01, 0x00, 0xc3, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x34, 0x10, 0x00, 0x3f, 0x08, 0x31, 0x27, 0x04, 0x10, 0x02, 0x12, 0x00, 0x2c, 0x03, 0xf9, 0x62, 0x11, 0x0e, 0x0e, 0x00, 0x02, 0xff, 0xff, 0x00, 0x27, 0x00, 0x00, 0x07, 0xff, 0x40, 0xcc, 0xa1, 0x30, 0xa0, 0x21, 0xd1, 0xb9, 0xc9, 0xea, 0x05, 0x12, 0x11, 0x0a, 0x04, 0x15, 0xfc, 0x03, 0x00, 0xcc, 0xa1, 0x30, 0xa0, 0x21, 0xd1, 0xb9, 0xc9, 0xea, 0x05, 0x12, 0x11, 0x0a, 0x04, 0x15, 0xfc, 0x03, 0x00, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+ // OOK_Rb10Bw40
+ { 0x01, 0x01, 0x86, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x32, 0x20, 0x00, 0x5e, 0x05, 0x76, 0x1a, 0x02, 0xb9, 0x02, 0x12, 0x00, 0x57, 0x02, 0xb0, 0x62, 0x11, 0x15, 0x15, 0x00, 0x02, 0xff, 0xff, 0x00, 0x28, 0x00, 0x00, 0x07, 0xff, 0x40, 0xa2, 0x81, 0x26, 0xaf, 0x3f, 0xee, 0xc8, 0xc7, 0xdb, 0xf2, 0x02, 0x08, 0x07, 0x03, 0x15, 0xfc, 0x0f, 0x00, 0xa2, 0x81, 0x26, 0xaf, 0x3f, 0xee, 0xc8, 0xc7, 0xdb, 0xf2, 0x02, 0x08, 0x07, 0x03, 0x15, 0xfc, 0x0f, 0x00, 0x3f, 0x2c, 0x0e, 0x04, 0x0c, 0x73, },
+
+};
+
+RH_RF24::RH_RF24(PINS slaveSelectPin, PINS interruptPin, PINS sdnPin, RHGenericSPI& spi)
+ :
+ RHSPIDriver(slaveSelectPin, spi),
+ _interruptPin(interruptPin),
+ _sdnPin(sdnPin)
+{
+ _idleMode = RH_RF24_DEVICE_STATE_READY;
+ _myInterruptIndex = 0xff; // Not allocated yet
+}
+
+void RH_RF24::setIdleMode(uint8_t idleMode)
+{
+ _idleMode = idleMode;
+}
+
+bool RH_RF24::init()
+{
+ if (!RHSPIDriver::init())
+ return false;
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Determine the interrupt number that corresponds to the interruptPin
+ int interruptNumber = digitalPinToInterrupt(_interruptPin);
+ if (interruptNumber == NOT_AN_INTERRUPT)
+ return false;
+#endif
+
+ // Initialise the radio
+ power_on_reset();
+ cmd_clear_all_interrupts();
+ // Here we use a configuration generated by the Silicon Las Wireless Development Suite
+ // in radio_config_Si4460.h
+ // WE override a few things later that we ned to be sure of.
+ configure(RFM26_CONFIGURATION_DATA);
+
+ // Get the device type and check it
+ // This also tests whether we are really connected to a device
+ uint8_t buf[8];
+ if (!command(RH_RF24_CMD_PART_INFO, 0, 0, buf, sizeof(buf)))
+ return false; // SPI error? Not connected?
+ _deviceType = (buf[1] << 8) | buf[2];
+ // Check PART to be either 0x4460, 0x4461, 0x4463, 0x4464
+ if (_deviceType != 0x4460 &&
+ _deviceType != 0x4461 &&
+ _deviceType != 0x4463 &&
+ _deviceType != 0x4464)
+ return false; // Unknown radio type, or not connected
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
+ // ARM M4 requires the below. else pin interrupt doesn't work properly.
+ // On all other platforms, its innocuous, belt and braces
+ pinMode(_interruptPin, INPUT);
+#endif
+
+ // Set up interrupt handler
+ // Since there are a limited number of interrupt glue functions isr*() available,
+ // we can only support a limited number of devices simultaneously
+ // ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
+ // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
+ // yourself based on knwledge of what Arduino board you are running on.
+ if (_myInterruptIndex == 0xff)
+ {
+ // First run, no interrupt allocated yet
+ if (_interruptCount <= RH_RF24_NUM_INTERRUPTS)
+ _myInterruptIndex = _interruptCount++;
+ else
+ return false; // Too many devices, not enough interrupt vectors
+ }
+ _deviceForInterrupt[_myInterruptIndex] = this;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ if (_myInterruptIndex == 0)
+ _interruptPin.fall(&isr0);
+ else if (_myInterruptIndex == 1)
+ _interruptPin.fall(&isr1);
+ else if (_myInterruptIndex == 2)
+ _interruptPin.fall(&isr2);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#else
+ if (_myInterruptIndex == 0)
+ attachInterrupt(interruptNumber, isr0, FALLING);
+ else if (_myInterruptIndex == 1)
+ attachInterrupt(interruptNumber, isr1, FALLING);
+ else if (_myInterruptIndex == 2)
+ attachInterrupt(interruptNumber, isr2, FALLING);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#endif
+ // Ensure we get the interrupts we need, irrespective of whats in the radio_config
+ uint8_t int_ctl[] = {RH_RF24_MODEM_INT_STATUS_EN | RH_RF24_PH_INT_STATUS_EN, 0xff, 0xff, 0x00 };
+ set_properties(RH_RF24_PROPERTY_INT_CTL_ENABLE, int_ctl, sizeof(int_ctl));
+
+ // RSSI Latching should be configured in MODEM_RSSI_CONTROL in radio_config
+
+ // PKT_TX_THRESHOLD and PKT_RX_THRESHOLD should be set to about 0x30 in radio_config
+
+ // Configure important RH_RF24 registers
+ // Here we set up the standard packet format for use by the RH_RF24 library:
+ // We will use FIFO Mode, with automatic packet generation
+ // We have 2 fields:
+ // Field 1 contains only the (variable) length of field 2, with CRC
+ // Field 2 contains the variable length payload and the CRC
+ // Hmmm, having no CRC on field 1 and CRC on field 2 causes CRC errors when resetting after an odd
+ // number of packets! Anyway its prob a good thing at the cost of some airtime.
+ // Hmmm, enabling WHITEN stops it working!
+ uint8_t pkt_config1[] = { 0x00 };
+ set_properties(RH_RF24_PROPERTY_PKT_CONFIG1, pkt_config1, sizeof(pkt_config1));
+
+ uint8_t pkt_len[] = { 0x02, 0x01, 0x00 };
+ set_properties(RH_RF24_PROPERTY_PKT_LEN, pkt_len, sizeof(pkt_len));
+
+ uint8_t pkt_field1[] = { 0x00, 0x01, 0x00, RH_RF24_FIELD_CONFIG_CRC_START | RH_RF24_FIELD_CONFIG_SEND_CRC | RH_RF24_FIELD_CONFIG_CHECK_CRC | RH_RF24_FIELD_CONFIG_CRC_ENABLE };
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8, pkt_field1, sizeof(pkt_field1));
+
+ uint8_t pkt_field2[] = { 0x00, sizeof(_buf), 0x00, RH_RF24_FIELD_CONFIG_CRC_START | RH_RF24_FIELD_CONFIG_SEND_CRC | RH_RF24_FIELD_CONFIG_CHECK_CRC | RH_RF24_FIELD_CONFIG_CRC_ENABLE };
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8, pkt_field2, sizeof(pkt_field2));
+
+ // Clear all other fields so they are never used, irrespective of the radio_config
+ uint8_t pkt_fieldn[] = { 0x00, 0x00, 0x00, 0x00 };
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn));
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn));
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn));
+
+ // The following can be changed later by the user if necessary.
+ // Set up default configuration
+ setCRCPolynomial(CRC_16_IBM);
+ uint8_t syncwords[] = { 0x2d, 0xd4 };
+ setSyncWords(syncwords, sizeof(syncwords)); // Same as RF22's
+ // Reasonably fast and reliable default speed and modulation
+ setModemConfig(GFSK_Rb5Fd10);
+ // 3 would be sufficient, but this is the same as RF22's
+ // actualy, 4 seems to work much better for some modulations
+ setPreambleLength(4);
+ // An innocuous ISM frequency, same as RF22's
+ setFrequency(434.0);
+ // About 2.4dBm on RFM24:
+ setTxPower(0x10);
+
+ return true;
+}
+
+// C++ level interrupt handler for this instance
+void RH_RF24::handleInterrupt()
+{
+ uint8_t status[8];
+ command(RH_RF24_CMD_GET_INT_STATUS, NULL, 0, status, sizeof(status));
+
+ // Decode and handle the interrupt bits we are interested in
+// if (status[0] & RH_RF24_INT_STATUS_CHIP_INT_STATUS)
+ if (status[0] & RH_RF24_INT_STATUS_MODEM_INT_STATUS)
+ {
+// if (status[4] & RH_RF24_INT_STATUS_INVALID_PREAMBLE)
+ if (status[4] & RH_RF24_INT_STATUS_INVALID_SYNC)
+ {
+ // After INVALID_SYNC, sometimes the radio gets into a silly state and subsequently reports it for every packet
+ // Need to reset the radio and clear the RX FIFO, cause sometimes theres junk there too
+ _mode = RHModeIdle;
+ clearRxFifo();
+ clearBuffer();
+ }
+ }
+ if (status[0] & RH_RF24_INT_STATUS_PH_INT_STATUS)
+ {
+ if (status[2] & RH_RF24_INT_STATUS_CRC_ERROR)
+ {
+ // CRC Error
+ // Radio automatically went to _idleMode
+ _mode = RHModeIdle;
+ _rxBad++;
+
+ clearRxFifo();
+ clearBuffer();
+ }
+ if (status[2] & RH_RF24_INT_STATUS_PACKET_SENT)
+ {
+ _txGood++;
+ // Transmission does not automatically clear the tx buffer.
+ // Could retransmit if we wanted
+ // RH_RF24 configured to transition automatically to Idle after packet sent
+ _mode = RHModeIdle;
+ clearBuffer();
+ }
+ if (status[2] & RH_RF24_INT_STATUS_PACKET_RX)
+ {
+ // A complete message has been received with good CRC
+ // Get the RSSI, configured to latch at sync detect in radio_config
+ uint8_t modem_status[6];
+ command(RH_RF24_CMD_GET_MODEM_STATUS, NULL, 0, modem_status, sizeof(modem_status));
+ _lastRssi = modem_status[3];
+ _lastPreambleTime = millis();
+
+ // Save it in our buffer
+ readNextFragment();
+ // And see if we have a valid message
+ validateRxBuf();
+ // Radio will have transitioned automatically to the _idleMode
+ _mode = RHModeIdle;
+ }
+ if (status[2] & RH_RF24_INT_STATUS_TX_FIFO_ALMOST_EMPTY)
+ {
+ // TX FIFO almost empty, maybe send another chunk, if there is one
+ sendNextFragment();
+ }
+ if (status[2] & RH_RF24_INT_STATUS_RX_FIFO_ALMOST_FULL)
+ {
+ // Some more data to read, get it
+ readNextFragment();
+ }
+ }
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_RF24::validateRxBuf()
+{
+ // Validate headers etc
+ if (_bufLen >= RH_RF24_HEADER_LEN)
+ {
+ _rxHeaderTo = _buf[0];
+ _rxHeaderFrom = _buf[1];
+ _rxHeaderId = _buf[2];
+ _rxHeaderFlags = _buf[3];
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ // Its for us
+ _rxGood++;
+ _rxBufValid = true;
+ }
+ }
+}
+
+bool RH_RF24::clearRxFifo()
+{
+ uint8_t fifo_clear[] = { 0x02 };
+ return command(RH_RF24_CMD_FIFO_INFO, fifo_clear, sizeof(fifo_clear));
+}
+
+void RH_RF24::clearBuffer()
+{
+ _bufLen = 0;
+ _txBufSentIndex = 0;
+ _rxBufValid = false;
+}
+
+// These are low level functions that call the interrupt handler for the correct
+// instance of RH_RF24.
+// 3 interrupts allows us to have 3 different devices
+void RH_RF24::isr0()
+{
+ if (_deviceForInterrupt[0])
+ _deviceForInterrupt[0]->handleInterrupt();
+}
+void RH_RF24::isr1()
+{
+ if (_deviceForInterrupt[1])
+ _deviceForInterrupt[1]->handleInterrupt();
+}
+void RH_RF24::isr2()
+{
+ if (_deviceForInterrupt[2])
+ _deviceForInterrupt[2]->handleInterrupt();
+}
+
+bool RH_RF24::available()
+{
+ if (_mode == RHModeTx)
+ return false;
+ if (!_rxBufValid)
+ setModeRx(); // Make sure we are receiving
+ return _rxBufValid;
+}
+
+bool RH_RF24::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+ // CAUTION: first 4 octets of _buf contain the headers
+ if (buf && len && (_bufLen >= RH_RF24_HEADER_LEN))
+ {
+ ATOMIC_BLOCK_START;
+ if (*len > _bufLen - RH_RF24_HEADER_LEN)
+ *len = _bufLen - RH_RF24_HEADER_LEN;
+ memcpy(buf, _buf + RH_RF24_HEADER_LEN, *len);
+ ATOMIC_BLOCK_END;
+ }
+ clearBuffer(); // Got the most recent message
+ return true;
+}
+
+bool RH_RF24::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_RF24_MAX_MESSAGE_LEN)
+ return false;
+
+ waitPacketSent(); // Make sure we dont interrupt an outgoing message
+ setModeIdle(); // Prevent RX while filling the fifo
+
+ // Put the payload in the FIFO
+ // First the length in fixed length field 1. This wont appear in the receiver fifo since
+ // we have turned off IN_FIFO in PKT_LEN
+ _buf[0] = len + RH_RF24_HEADER_LEN;
+ // Now the rest of the payload in variable length field 2
+ // First the headers
+ _buf[1] = _txHeaderTo;
+ _buf[2] = _txHeaderFrom;
+ _buf[3] = _txHeaderId;
+ _buf[4] = _txHeaderFlags;
+ // Then the message
+ memcpy(_buf + 1 + RH_RF24_HEADER_LEN, data, len);
+ _bufLen = len + 1 + RH_RF24_HEADER_LEN;
+ _txBufSentIndex = 0;
+
+ // Set the field 2 length to the variable payload length
+ uint8_t l[] = { len + RH_RF24_HEADER_LEN};
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0, l, sizeof(l));
+
+ sendNextFragment();
+ setModeTx();
+ return true;
+}
+
+// This is different to command() since we must not wait for CTS
+bool RH_RF24::writeTxFifo(uint8_t *data, uint8_t len)
+{
+ ATOMIC_BLOCK_START;
+ // First send the command
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(RH_RF24_CMD_TX_FIFO_WRITE);
+ // Now write any write data
+ while (len--)
+ _spi.transfer(*data++);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return true;
+}
+
+void RH_RF24::sendNextFragment()
+{
+ if (_txBufSentIndex < _bufLen)
+ {
+ // Some left to send?
+ uint8_t len = _bufLen - _txBufSentIndex;
+ // But dont send too much, see how much room is left
+ uint8_t fifo_info[2];
+ command(RH_RF24_CMD_FIFO_INFO, NULL, 0, fifo_info, sizeof(fifo_info));
+ // fifo_info[1] is space left in TX FIFO
+ if (len > fifo_info[1])
+ len = fifo_info[1];
+
+ writeTxFifo(_buf + _txBufSentIndex, len);
+ _txBufSentIndex += len;
+ }
+}
+
+void RH_RF24::readNextFragment()
+{
+ // Get the packet length from the RX FIFO length
+ uint8_t fifo_info[1];
+ command(RH_RF24_CMD_FIFO_INFO, NULL, 0, fifo_info, sizeof(fifo_info));
+ uint8_t fifo_len = fifo_info[0];
+
+ // Check for overflow
+ if ((_bufLen + fifo_len) > sizeof(_buf))
+ {
+ // Overflow pending
+ _rxBad++;
+ setModeIdle();
+ clearRxFifo();
+ clearBuffer();
+ return;
+ }
+ // So we have room
+ // Now read the fifo_len bytes from the RX FIFO
+ // This is different to command() since we dont wait for CTS
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(RH_RF24_CMD_RX_FIFO_READ);
+ uint8_t* p = _buf + _bufLen;
+ uint8_t l = fifo_len;
+ while (l--)
+ *p++ = _spi.transfer(0);
+ digitalWrite(_slaveSelectPin, HIGH);
+ _bufLen += fifo_len;
+}
+
+uint8_t RH_RF24::maxMessageLength()
+{
+ return RH_RF24_MAX_MESSAGE_LEN;
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF24::setModemRegisters(const ModemConfig* config)
+{
+ // This list also generated with convert.pl
+ set_properties(0x2000, &config->prop_2000, 1);
+ set_properties(0x2003, &config->prop_2003, 1);
+ set_properties(0x2004, &config->prop_2004, 1);
+ set_properties(0x2005, &config->prop_2005, 1);
+ set_properties(0x2006, &config->prop_2006, 1);
+ set_properties(0x200b, &config->prop_200b, 1);
+ set_properties(0x200c, &config->prop_200c, 1);
+ set_properties(0x2018, &config->prop_2018, 1);
+ set_properties(0x201e, &config->prop_201e, 1);
+ set_properties(0x201f, &config->prop_201f, 1);
+ set_properties(0x2022, &config->prop_2022, 1);
+ set_properties(0x2023, &config->prop_2023, 1);
+ set_properties(0x2024, &config->prop_2024, 1);
+ set_properties(0x2025, &config->prop_2025, 1);
+ set_properties(0x2026, &config->prop_2026, 1);
+ set_properties(0x2027, &config->prop_2027, 1);
+ set_properties(0x2028, &config->prop_2028, 1);
+ set_properties(0x2029, &config->prop_2029, 1);
+ set_properties(0x202d, &config->prop_202d, 1);
+ set_properties(0x202e, &config->prop_202e, 1);
+ set_properties(0x202f, &config->prop_202f, 1);
+ set_properties(0x2030, &config->prop_2030, 1);
+ set_properties(0x2031, &config->prop_2031, 1);
+ set_properties(0x2035, &config->prop_2035, 1);
+ set_properties(0x2038, &config->prop_2038, 1);
+ set_properties(0x2039, &config->prop_2039, 1);
+ set_properties(0x203a, &config->prop_203a, 1);
+ set_properties(0x203b, &config->prop_203b, 1);
+ set_properties(0x203c, &config->prop_203c, 1);
+ set_properties(0x203d, &config->prop_203d, 1);
+ set_properties(0x203e, &config->prop_203e, 1);
+ set_properties(0x203f, &config->prop_203f, 1);
+ set_properties(0x2040, &config->prop_2040, 1);
+ set_properties(0x2043, &config->prop_2043, 1);
+ set_properties(0x2045, &config->prop_2045, 1);
+ set_properties(0x2046, &config->prop_2046, 1);
+ set_properties(0x2047, &config->prop_2047, 1);
+ set_properties(0x204e, &config->prop_204e, 1);
+ set_properties(0x2100, &config->prop_2100, 1);
+ set_properties(0x2101, &config->prop_2101, 1);
+ set_properties(0x2102, &config->prop_2102, 1);
+ set_properties(0x2103, &config->prop_2103, 1);
+ set_properties(0x2104, &config->prop_2104, 1);
+ set_properties(0x2105, &config->prop_2105, 1);
+ set_properties(0x2106, &config->prop_2106, 1);
+ set_properties(0x2107, &config->prop_2107, 1);
+ set_properties(0x2108, &config->prop_2108, 1);
+ set_properties(0x2109, &config->prop_2109, 1);
+ set_properties(0x210a, &config->prop_210a, 1);
+ set_properties(0x210b, &config->prop_210b, 1);
+ set_properties(0x210c, &config->prop_210c, 1);
+ set_properties(0x210d, &config->prop_210d, 1);
+ set_properties(0x210e, &config->prop_210e, 1);
+ set_properties(0x210f, &config->prop_210f, 1);
+ set_properties(0x2110, &config->prop_2110, 1);
+ set_properties(0x2111, &config->prop_2111, 1);
+ set_properties(0x2112, &config->prop_2112, 1);
+ set_properties(0x2113, &config->prop_2113, 1);
+ set_properties(0x2114, &config->prop_2114, 1);
+ set_properties(0x2115, &config->prop_2115, 1);
+ set_properties(0x2116, &config->prop_2116, 1);
+ set_properties(0x2117, &config->prop_2117, 1);
+ set_properties(0x2118, &config->prop_2118, 1);
+ set_properties(0x2119, &config->prop_2119, 1);
+ set_properties(0x211a, &config->prop_211a, 1);
+ set_properties(0x211b, &config->prop_211b, 1);
+ set_properties(0x211c, &config->prop_211c, 1);
+ set_properties(0x211d, &config->prop_211d, 1);
+ set_properties(0x211e, &config->prop_211e, 1);
+ set_properties(0x211f, &config->prop_211f, 1);
+ set_properties(0x2120, &config->prop_2120, 1);
+ set_properties(0x2121, &config->prop_2121, 1);
+ set_properties(0x2122, &config->prop_2122, 1);
+ set_properties(0x2123, &config->prop_2123, 1);
+ set_properties(0x2203, &config->prop_2203, 1);
+ set_properties(0x2300, &config->prop_2300, 1);
+ set_properties(0x2301, &config->prop_2301, 1);
+ set_properties(0x2303, &config->prop_2303, 1);
+ set_properties(0x2304, &config->prop_2304, 1);
+ set_properties(0x2305, &config->prop_2305, 1);
+}
+
+// Set one of the canned Modem configs
+// Returns true if its a valid choice
+bool RH_RF24::setModemConfig(ModemConfigChoice index)
+{
+ if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
+ return false;
+
+ ModemConfig cfg;
+ memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF24::ModemConfig));
+ setModemRegisters(&cfg);
+
+ return true;
+}
+
+void RH_RF24::setPreambleLength(uint16_t bytes)
+{
+ uint8_t config[] = { bytes, 0x14, 0x00, 0x00,
+ RH_RF24_PREAMBLE_FIRST_1 | RH_RF24_PREAMBLE_LENGTH_BYTES | RH_RF24_PREAMBLE_STANDARD_1010};
+ set_properties(RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH, config, sizeof(config));
+}
+
+bool RH_RF24::setCRCPolynomial(CRCPolynomial polynomial)
+{
+ if (polynomial >= CRC_NONE &&
+ polynomial <= CRC_Castagnoli)
+ {
+ // Caution this only has effect if CRCs are enabled
+ uint8_t config[] = { (polynomial & RH_RF24_CRC_MASK) | RH_RF24_CRC_SEED_ALL_1S };
+ return set_properties(RH_RF24_PROPERTY_PKT_CRC_CONFIG, config, sizeof(config));
+ }
+ else
+ return false;
+}
+
+void RH_RF24::setSyncWords(const uint8_t* syncWords, uint8_t len)
+{
+ if (len > 4 || len < 1)
+ return;
+ uint8_t config[] = { len-1, 0, 0, 0, 0};
+ memcpy(config+1, syncWords, len);
+ set_properties(RH_RF24_PROPERTY_SYNC_CONFIG, config, sizeof(config));
+}
+
+bool RH_RF24::setFrequency(float centre, float afcPullInRange)
+{
+ // See Si446x Data Sheet section 5.3.1
+ // Also the Si446x PLL Synthesizer / VCO_CNT Calculator Rev 0.4
+ uint8_t outdiv;
+ uint8_t band;
+ if (_deviceType == 0x4460 ||
+ _deviceType == 0x4461 ||
+ _deviceType == 0x4463)
+ {
+ // Non-continuous frequency bands
+ if (centre <= 1050.0 && centre >= 850.0)
+ outdiv = 4, band = 0;
+ else if (centre <= 525.0 && centre >= 425.0)
+ outdiv = 8, band = 2;
+ else if (centre <= 350.0 && centre >= 284.0)
+ outdiv = 12, band = 3;
+ else if (centre <= 175.0 && centre >= 142.0)
+ outdiv = 24, band = 5;
+ else
+ return false;
+ }
+ else
+ {
+ // 0x4464
+ // Continuous frequency bands
+ if (centre <= 960.0 && centre >= 675.0)
+ outdiv = 4, band = 1;
+ else if (centre < 675.0 && centre >= 450.0)
+ outdiv = 6, band = 2;
+ else if (centre < 450.0 && centre >= 338.0)
+ outdiv = 8, band = 3;
+ else if (centre < 338.0 && centre >= 225.0)
+ outdiv = 12, band = 4;
+ else if (centre < 225.0 && centre >= 169.0)
+ outdiv = 16, band = 4;
+ else if (centre < 169.0 && centre >= 119.0)
+ outdiv = 24, band = 5;
+ else
+ return false;
+ }
+
+ // Set the MODEM_CLKGEN_BAND (not documented)
+ uint8_t modem_clkgen[] = { band+8 };
+ if (!set_properties(RH_RF24_PROPERTY_MODEM_CLKGEN_BAND, modem_clkgen, sizeof(modem_clkgen)))
+ return false;
+
+ centre *= 1000000.0; // Convert to Hz
+
+ // Now generate the RF frequency properties
+ // Need the Xtal/XO freq from the radio_config file:
+ uint32_t xtal_frequency[1] = RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ;
+ unsigned long f_pfd = 2 * xtal_frequency[0] / outdiv;
+ unsigned int n = ((unsigned int)(centre / f_pfd)) - 1;
+ float ratio = centre / (float)f_pfd;
+ float rest = ratio - (float)n;
+ unsigned long m = (unsigned long)(rest * 524288UL);
+ unsigned int m2 = m / 0x10000;
+ unsigned int m1 = (m - m2 * 0x10000) / 0x100;
+ unsigned int m0 = (m - m2 * 0x10000 - m1 * 0x100);
+
+ // PROP_FREQ_CONTROL_GROUP
+ uint8_t freq_control[] = { n, m2, m1, m0 };
+ return set_properties(RH_RF24_PROPERTY_FREQ_CONTROL_INTE, freq_control, sizeof(freq_control));
+}
+
+void RH_RF24::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch
+ uint8_t config[] = { RH_RF24_GPIO_HIGH, RH_RF24_GPIO_HIGH };
+ command(RH_RF24_CMD_GPIO_PIN_CFG, config, sizeof(config));
+
+ uint8_t state[] = { _idleMode };
+ command(RH_RF24_CMD_REQUEST_DEVICE_STATE, state, sizeof(state));
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_RF24::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ uint8_t state[] = { RH_RF24_DEVICE_STATE_SLEEP };
+ command(RH_RF24_CMD_REQUEST_DEVICE_STATE, state, sizeof(state));
+
+ _mode = RHModeSleep;
+ }
+ return true;
+}
+
+void RH_RF24::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ // CAUTION: we cant clear the rx buffers here, else we set up a race condition
+ // with the _rxBufValid test in available()
+
+ // Tell the receiver the max data length we will accept (a TX may have changed it)
+ uint8_t l[] = { sizeof(_buf) };
+ set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0, l, sizeof(l));
+
+ // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch
+ uint8_t gpio_config[] = { RH_RF24_GPIO_HIGH, RH_RF24_GPIO_LOW };
+ command(RH_RF24_CMD_GPIO_PIN_CFG, gpio_config, sizeof(gpio_config));
+
+ uint8_t rx_config[] = { 0x00, RH_RF24_CONDITION_RX_START_IMMEDIATE, 0x00, 0x00, _idleMode, _idleMode, _idleMode};
+ command(RH_RF24_CMD_START_RX, rx_config, sizeof(rx_config));
+ _mode = RHModeRx;
+ }
+}
+
+void RH_RF24::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch
+ uint8_t config[] = { RH_RF24_GPIO_LOW, RH_RF24_GPIO_HIGH };
+ command(RH_RF24_CMD_GPIO_PIN_CFG, config, sizeof(config));
+
+ uint8_t tx_params[] = { 0x00,
+ (uint8_t)(_idleMode << 4) | RH_RF24_CONDITION_RETRANSMIT_NO | RH_RF24_CONDITION_START_IMMEDIATE};
+ command(RH_RF24_CMD_START_TX, tx_params, sizeof(tx_params));
+ _mode = RHModeTx;
+ }
+}
+
+void RH_RF24::setTxPower(uint8_t power)
+{
+ uint8_t pa_bias_clkduty = 0;
+ // These calculations valid for advertised power from Si chips at Vcc = 3.3V
+ // you may get lower power from RFM modules, depending on Vcc voltage, antenna etc
+ if (_deviceType == 0x4460)
+ {
+ // 0x4f = 13dBm
+ pa_bias_clkduty = 0xc0;
+ if (power > 0x4f)
+ power = 0x4f;
+ }
+ else if (_deviceType == 0x4461)
+ {
+ // 0x7f = 16dBm
+ pa_bias_clkduty = 0xc0;
+ if (power > 0x7f)
+ power = 0x7f;
+ }
+ else if (_deviceType == 0x4463 || _deviceType == 0x4464 )
+ {
+ // 0x7f = 20dBm
+ pa_bias_clkduty = 0x00; // Per WDS suggestion
+ if (power > 0x7f)
+ power = 0x7f;
+ }
+ uint8_t power_properties[] = {0x18, 0x00, 0x00 }; // PA_MODE from WDS sugggestions (why?)
+ power_properties[1] = power;
+ power_properties[2] = pa_bias_clkduty;
+ set_properties(RH_RF24_PROPERTY_PA_MODE, power_properties, sizeof(power_properties));
+}
+
+// Caution: There was a bug in A1 hardware that will not handle 1 byte commands.
+bool RH_RF24::command(uint8_t cmd, const uint8_t* write_buf, uint8_t write_len, uint8_t* read_buf, uint8_t read_len)
+{
+ bool done = false;
+
+ ATOMIC_BLOCK_START;
+ // First send the command
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(cmd);
+
+ // Now write any write data
+ if (write_buf && write_len)
+ {
+ while (write_len--)
+ _spi.transfer(*write_buf++);
+ }
+ // Sigh, the RFM26 at least has problems if we deselect too quickly :-(
+ // Innocuous timewaster:
+ digitalWrite(_slaveSelectPin, LOW);
+ // And finalise the command
+ digitalWrite(_slaveSelectPin, HIGH);
+
+ uint16_t count; // Number of times we have tried to get CTS
+ for (count = 0; !done && count < RH_RF24_CTS_RETRIES; count++)
+ {
+ // Wait for the CTS
+ digitalWrite(_slaveSelectPin, LOW);
+
+ _spi.transfer(RH_RF24_CMD_READ_BUF);
+ if (_spi.transfer(0) == RH_RF24_REPLY_CTS)
+ {
+ // Now read any expected reply data
+ if (read_buf && read_len)
+ {
+ while (read_len--)
+ {
+ *read_buf++ = _spi.transfer(0);
+ }
+ }
+ done = true;
+ }
+ // Sigh, the RFM26 at least has problems if we deselect too quickly :-(
+ // Innocuous timewaster:
+ digitalWrite(_slaveSelectPin, LOW);
+ // Finalise the read
+ digitalWrite(_slaveSelectPin, HIGH);
+ }
+ ATOMIC_BLOCK_END;
+ return done; // False if too many attempts at CTS
+}
+
+bool RH_RF24::configure(const uint8_t* commands)
+{
+ // Command strings are constructed in radio_config_Si4460.h
+ // Each command starts with a count of the bytes in that command:
+ // <bytecount> <command> <bytecount-2 bytes of args/data>
+ uint8_t next_cmd_len;
+
+ while (memcpy_P(&next_cmd_len, commands, 1), next_cmd_len > 0)
+ {
+ uint8_t buf[20]; // As least big as the biggest permitted command/property list of 15
+ memcpy_P(buf, commands+1, next_cmd_len);
+ command(buf[0], buf+1, next_cmd_len - 1);
+ commands += (next_cmd_len + 1);
+ }
+ return true;
+}
+
+void RH_RF24::power_on_reset()
+{
+ // Sigh: its necessary to control the SDN pin to reset this ship.
+ // Tying it to GND does not produce reliable startups
+ // Per Si4464 Data Sheet 3.3.2
+ digitalWrite(_sdnPin, HIGH); // So we dont get a glitch after setting pinMode OUTPUT
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_sdnPin, OUTPUT);
+#endif
+ delay(10);
+ digitalWrite(_sdnPin, LOW);
+ delay(10);
+}
+
+bool RH_RF24::cmd_clear_all_interrupts()
+{
+ uint8_t write_buf[] = { 0x00, 0x00, 0x00 };
+ return command(RH_RF24_CMD_GET_INT_STATUS, write_buf, sizeof(write_buf));
+}
+
+bool RH_RF24::set_properties(uint16_t firstProperty, const uint8_t* values, uint8_t count)
+{
+ uint8_t buf[15];
+
+ buf[0] = firstProperty >> 8; // GROUP
+ buf[1] = count; // NUM_PROPS
+ buf[2] = firstProperty & 0xff; // START_PROP
+ uint8_t i;
+ for (i = 0; i < 12 && i < count; i++)
+ buf[3 + i] = values[i]; // DATAn
+ return command(RH_RF24_CMD_SET_PROPERTY, buf, count + 3);
+}
+
+bool RH_RF24::get_properties(uint16_t firstProperty, uint8_t* values, uint8_t count)
+{
+ if (count > 16)
+ count = 16;
+ uint8_t buf[3];
+ buf[0] = firstProperty >> 8; // GROUP
+ buf[1] = count; // NUM_PROPS
+ buf[2] = firstProperty & 0xff; // START_PROP
+ return command(RH_RF24_CMD_GET_PROPERTY, buf, sizeof(buf), values, count);
+}
+
+float RH_RF24::get_temperature()
+{
+ uint8_t write_buf[] = { 0x10 };
+ uint8_t read_buf[8];
+ // Takes nearly 4ms
+ command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf));
+ uint16_t temp_adc = (read_buf[4] << 8) | read_buf[5];
+ return ((800 + read_buf[6]) / 4096.0) * temp_adc - ((read_buf[7] / 2) + 256);
+}
+
+float RH_RF24::get_battery_voltage()
+{
+ uint8_t write_buf[] = { 0x08 };
+ uint8_t read_buf[8];
+ // Takes nearly 4ms
+ command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf));
+ uint16_t battery_adc = (read_buf[2] << 8) | read_buf[3];
+ return 3.0 * battery_adc / 1280;
+}
+
+float RH_RF24::get_gpio_voltage(uint8_t gpio)
+{
+ uint8_t write_buf[] = { 0x04 };
+ uint8_t read_buf[8];
+ write_buf[0] |= (gpio & 0x3);
+ // Takes nearly 4ms
+ command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf));
+ uint16_t gpio_adc = (read_buf[0] << 8) | read_buf[1];
+ return 3.0 * gpio_adc / 1280;
+}
+
+uint8_t RH_RF24::frr_read(uint8_t reg)
+{
+ uint8_t ret;
+
+ // Do not wait for CTS
+ ATOMIC_BLOCK_START;
+ // First send the command
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(RH_RF24_PROPERTY_FRR_CTL_A_MODE + reg);
+ // Get the fast response
+ ret = _spi.transfer(0);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ return ret;
+}
+
+// List of command replies to be printed by prinRegisters()
+PROGMEM static const RH_RF24::CommandInfo commands[] =
+{
+ { RH_RF24_CMD_PART_INFO, 8 },
+ { RH_RF24_CMD_FUNC_INFO, 6 },
+ { RH_RF24_CMD_GPIO_PIN_CFG, 7 },
+ { RH_RF24_CMD_FIFO_INFO, 2 },
+ { RH_RF24_CMD_PACKET_INFO, 2 },
+ { RH_RF24_CMD_GET_INT_STATUS, 8 },
+ { RH_RF24_CMD_GET_PH_STATUS, 2 },
+ { RH_RF24_CMD_GET_MODEM_STATUS, 8 },
+ { RH_RF24_CMD_GET_CHIP_STATUS, 3 },
+ { RH_RF24_CMD_REQUEST_DEVICE_STATE, 2 },
+};
+#define NUM_COMMAND_INFO (sizeof(commands)/sizeof(CommandInfo))
+
+// List of properties to be printed by printRegisters()
+PROGMEM static const uint16_t properties[] =
+{
+ RH_RF24_PROPERTY_GLOBAL_XO_TUNE,
+ RH_RF24_PROPERTY_GLOBAL_CLK_CFG,
+ RH_RF24_PROPERTY_GLOBAL_LOW_BATT_THRESH,
+ RH_RF24_PROPERTY_GLOBAL_CONFIG,
+ RH_RF24_PROPERTY_GLOBAL_WUT_CONFIG,
+ RH_RF24_PROPERTY_GLOBAL_WUT_M_15_8,
+ RH_RF24_PROPERTY_GLOBAL_WUT_M_7_0,
+ RH_RF24_PROPERTY_GLOBAL_WUT_R,
+ RH_RF24_PROPERTY_GLOBAL_WUT_LDC,
+ RH_RF24_PROPERTY_INT_CTL_ENABLE,
+ RH_RF24_PROPERTY_INT_CTL_PH_ENABLE,
+ RH_RF24_PROPERTY_INT_CTL_MODEM_ENABLE,
+ RH_RF24_PROPERTY_INT_CTL_CHIP_ENABLE,
+ RH_RF24_PROPERTY_FRR_CTL_A_MODE,
+ RH_RF24_PROPERTY_FRR_CTL_B_MODE,
+ RH_RF24_PROPERTY_FRR_CTL_C_MODE,
+ RH_RF24_PROPERTY_FRR_CTL_D_MODE,
+ RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH,
+ RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_1,
+ RH_RF24_PROPERTY_PREAMBLE_CONFIG_NSTD,
+ RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_2,
+ RH_RF24_PROPERTY_PREAMBLE_CONFIG,
+ RH_RF24_PROPERTY_PREAMBLE_PATTERN_31_24,
+ RH_RF24_PROPERTY_PREAMBLE_PATTERN_23_16,
+ RH_RF24_PROPERTY_PREAMBLE_PATTERN_15_8,
+ RH_RF24_PROPERTY_PREAMBLE_PATTERN_7_0,
+ RH_RF24_PROPERTY_SYNC_CONFIG,
+ RH_RF24_PROPERTY_SYNC_BITS_31_24,
+ RH_RF24_PROPERTY_SYNC_BITS_23_16,
+ RH_RF24_PROPERTY_SYNC_BITS_15_8,
+ RH_RF24_PROPERTY_SYNC_BITS_7_0,
+ RH_RF24_PROPERTY_PKT_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_CONFIG1,
+ RH_RF24_PROPERTY_PKT_LEN,
+ RH_RF24_PROPERTY_PKT_LEN_FIELD_SOURCE,
+ RH_RF24_PROPERTY_PKT_LEN_ADJUST,
+ RH_RF24_PROPERTY_PKT_TX_THRESHOLD,
+ RH_RF24_PROPERTY_PKT_RX_THRESHOLD,
+ RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_1_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_2_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_3_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_4_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG,
+ RH_RF24_PROPERTY_PKT_FIELD_5_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_1_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_2_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_3_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_4_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_12_8,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_7_0,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_5_CONFIG,
+ RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG,
+ RH_RF24_PROPERTY_MODEM_MOD_TYPE,
+ RH_RF24_PROPERTY_MODEM_MAP_CONTROL,
+ RH_RF24_PROPERTY_MODEM_DSM_CTRL,
+ RH_RF24_PROPERTY_MODEM_DATA_RATE_2,
+ RH_RF24_PROPERTY_MODEM_DATA_RATE_1,
+ RH_RF24_PROPERTY_MODEM_DATA_RATE_0,
+ RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3,
+ RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_2,
+ RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_1,
+ RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_0,
+ RH_RF24_PROPERTY_MODEM_FREQ_DEV_2,
+ RH_RF24_PROPERTY_MODEM_FREQ_DEV_1,
+ RH_RF24_PROPERTY_MODEM_FREQ_DEV_0,
+ RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY,
+ RH_RF24_PROPERTY_MODEM_MDM_CTRL,
+ RH_RF24_PROPERTY_MODEM_IF_CONTROL,
+ RH_RF24_PROPERTY_MODEM_IF_FREQ_2,
+ RH_RF24_PROPERTY_MODEM_IF_FREQ_1,
+ RH_RF24_PROPERTY_MODEM_IF_FREQ_0,
+ RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1,
+ RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0,
+ RH_RF24_PROPERTY_MODEM_BCR_OSR_1,
+ RH_RF24_PROPERTY_MODEM_BCR_OSR_0,
+ RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2,
+ RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1,
+ RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0,
+ RH_RF24_PROPERTY_MODEM_BCR_GAIN_1,
+ RH_RF24_PROPERTY_MODEM_BCR_GAIN_0,
+ RH_RF24_PROPERTY_MODEM_BCR_GEAR,
+ RH_RF24_PROPERTY_MODEM_BCR_MISC1,
+ RH_RF24_PROPERTY_MODEM_AFC_GEAR,
+ RH_RF24_PROPERTY_MODEM_AFC_WAIT,
+ RH_RF24_PROPERTY_MODEM_AFC_GAIN_1,
+ RH_RF24_PROPERTY_MODEM_AFC_GAIN_0,
+ RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1,
+ RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0,
+ RH_RF24_PROPERTY_MODEM_AFC_MISC,
+ RH_RF24_PROPERTY_MODEM_AGC_CONTROL,
+ RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE,
+ RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY,
+ RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY,
+ RH_RF24_PROPERTY_MODEM_FSK4_GAIN1,
+ RH_RF24_PROPERTY_MODEM_FSK4_GAIN0,
+ RH_RF24_PROPERTY_MODEM_FSK4_TH1,
+ RH_RF24_PROPERTY_MODEM_FSK4_TH0,
+ RH_RF24_PROPERTY_MODEM_FSK4_MAP,
+ RH_RF24_PROPERTY_MODEM_OOK_PDTC,
+ RH_RF24_PROPERTY_MODEM_OOK_CNT1,
+ RH_RF24_PROPERTY_MODEM_OOK_MISC,
+ RH_RF24_PROPERTY_MODEM_RAW_SEARCH,
+ RH_RF24_PROPERTY_MODEM_RAW_CONTROL,
+ RH_RF24_PROPERTY_MODEM_RAW_EYE_1,
+ RH_RF24_PROPERTY_MODEM_RAW_EYE_0,
+ RH_RF24_PROPERTY_MODEM_ANT_DIV_MODE,
+ RH_RF24_PROPERTY_MODEM_ANT_DIV_CONTROL,
+ RH_RF24_PROPERTY_MODEM_RSSI_THRESH,
+ RH_RF24_PROPERTY_MODEM_RSSI_JUMP_THRESH,
+ RH_RF24_PROPERTY_MODEM_RSSI_CONTROL,
+ RH_RF24_PROPERTY_MODEM_RSSI_CONTROL2,
+ RH_RF24_PROPERTY_MODEM_RSSI_COMP,
+ RH_RF24_PROPERTY_MODEM_ANT_DIV_CONT,
+ RH_RF24_PROPERTY_MODEM_CLKGEN_BAND,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2,
+ RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3,
+ RH_RF24_PROPERTY_PA_MODE,
+ RH_RF24_PROPERTY_PA_PWR_LVL,
+ RH_RF24_PROPERTY_PA_BIAS_CLKDUTY,
+ RH_RF24_PROPERTY_PA_TC,
+ RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF,
+ RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT,
+ RH_RF24_PROPERTY_SYNTH_VCO_KV,
+ RH_RF24_PROPERTY_SYNTH_LPFILT3,
+ RH_RF24_PROPERTY_SYNTH_LPFILT2,
+ RH_RF24_PROPERTY_SYNTH_LPFILT1,
+ RH_RF24_PROPERTY_SYNTH_LPFILT0,
+ RH_RF24_PROPERTY_MATCH_VALUE_1,
+ RH_RF24_PROPERTY_MATCH_MASK_1,
+ RH_RF24_PROPERTY_MATCH_CTRL_1,
+ RH_RF24_PROPERTY_MATCH_VALUE_2,
+ RH_RF24_PROPERTY_MATCH_MASK_2,
+ RH_RF24_PROPERTY_MATCH_CTRL_2,
+ RH_RF24_PROPERTY_MATCH_VALUE_3,
+ RH_RF24_PROPERTY_MATCH_MASK_3,
+ RH_RF24_PROPERTY_MATCH_CTRL_3,
+ RH_RF24_PROPERTY_MATCH_VALUE_4,
+ RH_RF24_PROPERTY_MATCH_MASK_4,
+ RH_RF24_PROPERTY_MATCH_CTRL_4,
+ RH_RF24_PROPERTY_FREQ_CONTROL_INTE,
+ RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_2,
+ RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_1,
+ RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_0,
+ RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_1,
+ RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_0,
+ RH_RF24_PROPERTY_FREQ_CONTROL_VCOCNT_RX_ADJ,
+ RH_RF24_PROPERTY_RX_HOP_CONTROL,
+ RH_RF24_PROPERTY_RX_HOP_TABLE_SIZE,
+ RH_RF24_PROPERTY_RX_HOP_TABLE_ENTRY_0,
+};
+#define NUM_PROPERTIES (sizeof(properties)/sizeof(uint16_t))
+
+bool RH_RF24::printRegisters()
+{
+#ifdef RH_HAVE_SERIAL
+ uint8_t i;
+ // First print the commands that return interesting data
+ for (i = 0; i < NUM_COMMAND_INFO; i++)
+ {
+ CommandInfo cmd;
+ memcpy_P(&cmd, &commands[i], sizeof(cmd));
+ uint8_t buf[10]; // Big enough for the biggest command reply
+ if (command(cmd.cmd, NULL, 0, buf, cmd.replyLen))
+ {
+ // Print the results:
+ Serial.print("cmd: ");
+ Serial.print(cmd.cmd, HEX);
+ Serial.print(" : ");
+ uint8_t j;
+ for (j = 0; j < cmd.replyLen; j++)
+ {
+ Serial.print(buf[j], HEX);
+ Serial.print(" ");
+ }
+ Serial.println("");
+ }
+ }
+
+ // Now print the properties
+ for (i = 0; i < NUM_PROPERTIES; i++)
+ {
+ uint16_t prop;
+ memcpy_P(&prop, &properties[i], sizeof(prop));
+ uint8_t result;
+ get_properties(prop, &result, 1);
+ Serial.print("prop: ");
+ Serial.print(prop, HEX);
+ Serial.print(": ");
+ Serial.print(result, HEX);
+ Serial.println("");
+ }
+#endif
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF24.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,1103 @@
+// RH_RF24.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_RF24.h,v 1.13 2015/08/13 02:45:47 mikem Exp $
+//
+// Supports RF24/RF26 and RFM24/RFM26 modules in FIFO mode
+// also Si4464/63/62/61/60-A1
+// Si4063 is the same but Tx only
+//
+// Per http://www.hoperf.cn/upload/rf/RFM24.pdf
+// and http://www.hoperf.cn/upload/rf/RFM26.pdf
+// Sigh: the HopeRF documentation is utter rubbish: full of errors and incomplete. The Si446x docs are better:
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/Si4464-63-61-60.pdf
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN626.pdf
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN627.pdf
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN647.pdf
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN633.pdf
+// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN736.pdf
+// http://nicerf.com/manage/upfile/indexbanner/635231050196868750.pdf (API description)
+// http://www.silabs.com/Support%20Documents/Software/Si446x%20RX_HOP%20PLL%20Calculator.xlsx
+#ifndef RH_RF24_h
+#define RH_RF24_h
+
+#include <RHGenericSPI.h>
+#include <RHSPIDriver.h>
+
+// This is the maximum number of interrupts the driver can support
+// Most Arduinos can handle 2, Megas can handle more
+#define RH_RF24_NUM_INTERRUPTS 3
+
+// Maximum payload length the RF24 can support, limited by our 1 octet message length
+#define RH_RF24_MAX_PAYLOAD_LEN 255
+
+// The length of the headers we add.
+// The headers are inside the RF24's payload
+#define RH_RF24_HEADER_LEN 4
+
+// This is the maximum message length that can be supported by this driver.
+// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
+// Here we allow for message length 4 bytes of address and header and payload to be included in payload size limit.
+#ifndef RH_RF24_MAX_MESSAGE_LEN
+#define RH_RF24_MAX_MESSAGE_LEN (RH_RF24_MAX_PAYLOAD_LEN - RH_RF24_HEADER_LEN - 1)
+#endif
+
+// Max number of times we will try to read CTS from the radio
+#define RH_RF24_CTS_RETRIES 2500
+
+// RF24/RF26 API commands from table 10
+// also Si446X API DESCRIPTIONS table 1
+#define RH_RF24_CMD_NOP 0x00
+#define RH_RF24_CMD_PART_INFO 0x01
+#define RH_RF24_CMD_POWER_UP 0x02
+#define RH_RF24_CMD_PATCH_IMAGE 0x04
+#define RH_RF24_CMD_FUNC_INFO 0x10
+#define RH_RF24_CMD_SET_PROPERTY 0x11
+#define RH_RF24_CMD_GET_PROPERTY 0x12
+#define RH_RF24_CMD_GPIO_PIN_CFG 0x13
+#define RH_RF24_CMD_GET_ADC_READING 0x14
+#define RH_RF24_CMD_FIFO_INFO 0x15
+#define RH_RF24_CMD_PACKET_INFO 0x16
+#define RH_RF24_CMD_IRCAL 0x17
+#define RH_RF24_CMD_PROTOCOL_CFG 0x18
+#define RH_RF24_CMD_GET_INT_STATUS 0x20
+#define RH_RF24_CMD_GET_PH_STATUS 0x21
+#define RH_RF24_CMD_GET_MODEM_STATUS 0x22
+#define RH_RF24_CMD_GET_CHIP_STATUS 0x23
+#define RH_RF24_CMD_START_TX 0x31
+#define RH_RF24_CMD_START_RX 0x32
+#define RH_RF24_CMD_REQUEST_DEVICE_STATE 0x33
+#define RH_RF24_CMD_CHANGE_STATE 0x34
+#define RH_RF24_CMD_RX_HOP 0x36
+#define RH_RF24_CMD_READ_BUF 0x44
+#define RH_RF24_CMD_FAST_RESPONSE_A 0x50
+#define RH_RF24_CMD_FAST_RESPONSE_B 0x51
+#define RH_RF24_CMD_FAST_RESPONSE_C 0x53
+#define RH_RF24_CMD_FAST_RESPONSE_D 0x57
+#define RH_RF24_CMD_TX_FIFO_WRITE 0x66
+#define RH_RF24_CMD_RX_FIFO_READ 0x77
+
+// The Clear To Send signal from the radio
+#define RH_RF24_REPLY_CTS 0xff
+
+//#define RH_RF24_CMD_START_TX 0x31
+#define RH_RF24_CONDITION_TX_COMPLETE_STATE 0xf0
+#define RH_RF24_CONDITION_RETRANSMIT_NO 0x00
+#define RH_RF24_CONDITION_RETRANSMIT_YES 0x04
+#define RH_RF24_CONDITION_START_IMMEDIATE 0x00
+#define RH_RF24_CONDITION_START_AFTER_WUT 0x01
+
+//#define RH_RF24_CMD_START_RX 0x32
+#define RH_RF24_CONDITION_RX_START_IMMEDIATE 0x00
+
+//#define RH_RF24_CMD_REQUEST_DEVICE_STATE 0x33
+#define RH_RF24_DEVICE_STATE_NO_CHANGE 0x00
+#define RH_RF24_DEVICE_STATE_SLEEP 0x01
+#define RH_RF24_DEVICE_STATE_SPI_ACTIVE 0x02
+#define RH_RF24_DEVICE_STATE_READY 0x03
+#define RH_RF24_DEVICE_STATE_ALSO_READY 0x04
+#define RH_RF24_DEVICE_STATE_TUNE_TX 0x05
+#define RH_RF24_DEVICE_STATE_TUNE_RX 0x06
+#define RH_RF24_DEVICE_STATE_TX 0x07
+#define RH_RF24_DEVICE_STATE_RX 0x08
+
+// Properties for API Description AN625 Section 2.2
+#define RH_RF24_PROPERTY_GLOBAL_XO_TUNE 0x0000
+#define RH_RF24_PROPERTY_GLOBAL_CLK_CFG 0x0001
+#define RH_RF24_PROPERTY_GLOBAL_LOW_BATT_THRESH 0x0002
+#define RH_RF24_PROPERTY_GLOBAL_CONFIG 0x0003
+#define RH_RF24_PROPERTY_GLOBAL_WUT_CONFIG 0x0004
+#define RH_RF24_PROPERTY_GLOBAL_WUT_M_15_8 0x0005
+#define RH_RF24_PROPERTY_GLOBAL_WUT_M_7_0 0x0006
+#define RH_RF24_PROPERTY_GLOBAL_WUT_R 0x0007
+#define RH_RF24_PROPERTY_GLOBAL_WUT_LDC 0x0008
+#define RH_RF24_PROPERTY_INT_CTL_ENABLE 0x0100
+#define RH_RF24_PROPERTY_INT_CTL_PH_ENABLE 0x0101
+#define RH_RF24_PROPERTY_INT_CTL_MODEM_ENABLE 0x0102
+#define RH_RF24_PROPERTY_INT_CTL_CHIP_ENABLE 0x0103
+#define RH_RF24_PROPERTY_FRR_CTL_A_MODE 0x0200
+#define RH_RF24_PROPERTY_FRR_CTL_B_MODE 0x0201
+#define RH_RF24_PROPERTY_FRR_CTL_C_MODE 0x0202
+#define RH_RF24_PROPERTY_FRR_CTL_D_MODE 0x0203
+#define RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH 0x1000
+#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_1 0x1001
+#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_NSTD 0x1002
+#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_2 0x1003
+#define RH_RF24_PROPERTY_PREAMBLE_CONFIG 0x1004
+#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_31_24 0x1005
+#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_23_16 0x1006
+#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_15_8 0x1007
+#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_7_0 0x1008
+#define RH_RF24_PROPERTY_SYNC_CONFIG 0x1100
+#define RH_RF24_PROPERTY_SYNC_BITS_31_24 0x1101
+#define RH_RF24_PROPERTY_SYNC_BITS_23_16 0x1102
+#define RH_RF24_PROPERTY_SYNC_BITS_15_8 0x1103
+#define RH_RF24_PROPERTY_SYNC_BITS_7_0 0x1104
+#define RH_RF24_PROPERTY_PKT_CRC_CONFIG 0x1200
+#define RH_RF24_PROPERTY_PKT_CONFIG1 0x1206
+#define RH_RF24_PROPERTY_PKT_LEN 0x1208
+#define RH_RF24_PROPERTY_PKT_LEN_FIELD_SOURCE 0x1209
+#define RH_RF24_PROPERTY_PKT_LEN_ADJUST 0x120a
+#define RH_RF24_PROPERTY_PKT_TX_THRESHOLD 0x120b
+#define RH_RF24_PROPERTY_PKT_RX_THRESHOLD 0x120c
+#define RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8 0x120d
+#define RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_7_0 0x120e
+#define RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG 0x120f
+#define RH_RF24_PROPERTY_PKT_FIELD_1_CRC_CONFIG 0x1210
+#define RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8 0x1211
+#define RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0 0x1212
+#define RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG 0x1213
+#define RH_RF24_PROPERTY_PKT_FIELD_2_CRC_CONFIG 0x1214
+#define RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8 0x1215
+#define RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_7_0 0x1216
+#define RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG 0x1217
+#define RH_RF24_PROPERTY_PKT_FIELD_3_CRC_CONFIG 0x1218
+#define RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8 0x1219
+#define RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_7_0 0x121a
+#define RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG 0x121b
+#define RH_RF24_PROPERTY_PKT_FIELD_4_CRC_CONFIG 0x121c
+#define RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8 0x121d
+#define RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_7_0 0x121e
+#define RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG 0x121f
+#define RH_RF24_PROPERTY_PKT_FIELD_5_CRC_CONFIG 0x1220
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_12_8 0x1221
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_7_0 0x1222
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CONFIG 0x1223
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG 0x1224
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_12_8 0x1225
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_7_0 0x1226
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CONFIG 0x1227
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG 0x1228
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_12_8 0x1229
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_7_0 0x122a
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CONFIG 0x122b
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG 0x122c
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_12_8 0x122d
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_7_0 0x122e
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CONFIG 0x122f
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG 0x1230
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_12_8 0x1231
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_7_0 0x1232
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CONFIG 0x1233
+#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG 0x1234
+#define RH_RF24_PROPERTY_MODEM_MOD_TYPE 0x2000
+#define RH_RF24_PROPERTY_MODEM_MAP_CONTROL 0x2001
+#define RH_RF24_PROPERTY_MODEM_DSM_CTRL 0x2002
+#define RH_RF24_PROPERTY_MODEM_DATA_RATE_2 0x2003
+#define RH_RF24_PROPERTY_MODEM_DATA_RATE_1 0x2004
+#define RH_RF24_PROPERTY_MODEM_DATA_RATE_0 0x2005
+#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3 0x2006
+#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_2 0x2007
+#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_1 0x2008
+#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_0 0x2009
+#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_2 0x200a
+#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_1 0x200b
+#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_0 0x200c
+#define RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY 0x2018
+#define RH_RF24_PROPERTY_MODEM_MDM_CTRL 0x2019
+#define RH_RF24_PROPERTY_MODEM_IF_CONTROL 0x201a
+#define RH_RF24_PROPERTY_MODEM_IF_FREQ_2 0x201b
+#define RH_RF24_PROPERTY_MODEM_IF_FREQ_1 0x201c
+#define RH_RF24_PROPERTY_MODEM_IF_FREQ_0 0x201d
+#define RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1 0x201e
+#define RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0 0x201f
+#define RH_RF24_PROPERTY_MODEM_BCR_OSR_1 0x2022
+#define RH_RF24_PROPERTY_MODEM_BCR_OSR_0 0x2023
+#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2 0x2024
+#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1 0x2025
+#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0 0x2026
+#define RH_RF24_PROPERTY_MODEM_BCR_GAIN_1 0x2027
+#define RH_RF24_PROPERTY_MODEM_BCR_GAIN_0 0x2028
+#define RH_RF24_PROPERTY_MODEM_BCR_GEAR 0x2029
+#define RH_RF24_PROPERTY_MODEM_BCR_MISC1 0x202a
+#define RH_RF24_PROPERTY_MODEM_AFC_GEAR 0x202c
+#define RH_RF24_PROPERTY_MODEM_AFC_WAIT 0x202d
+#define RH_RF24_PROPERTY_MODEM_AFC_GAIN_1 0x202e
+#define RH_RF24_PROPERTY_MODEM_AFC_GAIN_0 0x202f
+#define RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1 0x2030
+#define RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0 0x2031
+#define RH_RF24_PROPERTY_MODEM_AFC_MISC 0x2032
+#define RH_RF24_PROPERTY_MODEM_AGC_CONTROL 0x2035
+#define RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE 0x2038
+#define RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY 0x2039
+#define RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY 0x203a
+#define RH_RF24_PROPERTY_MODEM_FSK4_GAIN1 0x203b
+#define RH_RF24_PROPERTY_MODEM_FSK4_GAIN0 0x203c
+#define RH_RF24_PROPERTY_MODEM_FSK4_TH1 0x203d
+#define RH_RF24_PROPERTY_MODEM_FSK4_TH0 0x203e
+#define RH_RF24_PROPERTY_MODEM_FSK4_MAP 0x203f
+#define RH_RF24_PROPERTY_MODEM_OOK_PDTC 0x2040
+#define RH_RF24_PROPERTY_MODEM_OOK_CNT1 0x2042
+#define RH_RF24_PROPERTY_MODEM_OOK_MISC 0x2043
+#define RH_RF24_PROPERTY_MODEM_RAW_SEARCH 0x2044
+#define RH_RF24_PROPERTY_MODEM_RAW_CONTROL 0x2045
+#define RH_RF24_PROPERTY_MODEM_RAW_EYE_1 0x2046
+#define RH_RF24_PROPERTY_MODEM_RAW_EYE_0 0x2047
+#define RH_RF24_PROPERTY_MODEM_ANT_DIV_MODE 0x2048
+#define RH_RF24_PROPERTY_MODEM_ANT_DIV_CONTROL 0x2049
+#define RH_RF24_PROPERTY_MODEM_RSSI_THRESH 0x204a
+#define RH_RF24_PROPERTY_MODEM_RSSI_JUMP_THRESH 0x204b
+#define RH_RF24_PROPERTY_MODEM_RSSI_CONTROL 0x204c
+#define RH_RF24_PROPERTY_MODEM_RSSI_CONTROL2 0x204d
+#define RH_RF24_PROPERTY_MODEM_RSSI_COMP 0x204e
+#define RH_RF24_PROPERTY_MODEM_ANT_DIV_CONT 0x2049
+#define RH_RF24_PROPERTY_MODEM_CLKGEN_BAND 0x2051
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0 0x2100
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0 0x2101
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0 0x2102
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0 0x2103
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0 0x2104
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0 0x2105
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0 0x2106
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0 0x2107
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0 0x2108
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0 0x2109
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0 0x210a
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0 0x210b
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0 0x210c
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0 0x210d
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0 0x210e
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1 0x210f
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2 0x2110
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3 0x2111
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0 0x2112
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0 0x2113
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0 0x2114
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0 0x2115
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0 0x2116
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0 0x2117
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0 0x2118
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0 0x2119
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0 0x211a
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0 0x211b
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0 0x211c
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0 0x211d
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0 0x211e
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0 0x211f
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0 0x2120
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1 0x2121
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2 0x2122
+#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3 0x2123
+#define RH_RF24_PROPERTY_PA_MODE 0x2200
+#define RH_RF24_PROPERTY_PA_PWR_LVL 0x2201
+#define RH_RF24_PROPERTY_PA_BIAS_CLKDUTY 0x2202
+#define RH_RF24_PROPERTY_PA_TC 0x2203
+#define RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF 0x2300
+#define RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT 0x2301
+#define RH_RF24_PROPERTY_SYNTH_VCO_KV 0x2302
+#define RH_RF24_PROPERTY_SYNTH_LPFILT3 0x2303
+#define RH_RF24_PROPERTY_SYNTH_LPFILT2 0x2304
+#define RH_RF24_PROPERTY_SYNTH_LPFILT1 0x2305
+#define RH_RF24_PROPERTY_SYNTH_LPFILT0 0x2306
+#define RH_RF24_PROPERTY_MATCH_VALUE_1 0x3000
+#define RH_RF24_PROPERTY_MATCH_MASK_1 0x3001
+#define RH_RF24_PROPERTY_MATCH_CTRL_1 0x3002
+#define RH_RF24_PROPERTY_MATCH_VALUE_2 0x3003
+#define RH_RF24_PROPERTY_MATCH_MASK_2 0x3004
+#define RH_RF24_PROPERTY_MATCH_CTRL_2 0x3005
+#define RH_RF24_PROPERTY_MATCH_VALUE_3 0x3006
+#define RH_RF24_PROPERTY_MATCH_MASK_3 0x3007
+#define RH_RF24_PROPERTY_MATCH_CTRL_3 0x3008
+#define RH_RF24_PROPERTY_MATCH_VALUE_4 0x3009
+#define RH_RF24_PROPERTY_MATCH_MASK_4 0x300a
+#define RH_RF24_PROPERTY_MATCH_CTRL_4 0x300b
+#define RH_RF24_PROPERTY_FREQ_CONTROL_INTE 0x4000
+#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_2 0x4001
+#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_1 0x4002
+#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_0 0x4003
+#define RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_1 0x4004
+#define RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_0 0x4005
+#define RH_RF24_PROPERTY_FREQ_CONTROL_VCOCNT_RX_ADJ 0x4007
+#define RH_RF24_PROPERTY_RX_HOP_CONTROL 0x5000
+#define RH_RF24_PROPERTY_RX_HOP_TABLE_SIZE 0x5001
+#define RH_RF24_PROPERTY_RX_HOP_TABLE_ENTRY_0 0x5002
+
+//#define RH_RF24_CMD_GPIO_PIN_CFG 0x13
+#define RH_RF24_GPIO_NO_CHANGE 0
+#define RH_RF24_GPIO_DISABLED 1
+#define RH_RF24_GPIO_LOW 2
+#define RH_RF24_GPIO_HIGH 3
+#define RH_RF24_GPIO_INPUT 4
+#define RH_RF24_GPIO_32_KHZ_CLOCK 5
+#define RH_RF24_GPIO_BOOT_CLOCK 6
+#define RH_RF24_GPIO_DIVIDED_MCU_CLOCK 7
+#define RH_RF24_GPIO_CTS 8
+#define RH_RF24_GPIO_INV_CTS 9
+#define RH_RF24_GPIO_HIGH_ON_CMD_OVERLAP 10
+#define RH_RF24_GPIO_SPI_DATA_OUT 11
+#define RH_RF24_GPIO_HIGH_AFTER_RESET 12
+#define RH_RF24_GPIO_HIGH_AFTER_CALIBRATION 13
+#define RH_RF24_GPIO_HIGH_AFTER_WUT 14
+#define RH_RF24_GPIO_UNUSED_0 15
+#define RH_RF24_GPIO_TX_DATA_CLOCK 16
+#define RH_RF24_GPIO_RX_DATA_CLOCK 17
+#define RH_RF24_GPIO_UNUSED_1 18
+#define RH_RF24_GPIO_TX_DATA 19
+#define RH_RF24_GPIO_RX_DATA 20
+#define RH_RF24_GPIO_RX_RAW_DATA 21
+#define RH_RF24_GPIO_ANTENNA_1_SWITCH 22
+#define RH_RF24_GPIO_ANTENNA_2_SWITCH 23
+#define RH_RF24_GPIO_VALID_PREAMBLE 24
+#define RH_RF24_GPIO_INVALID_PREAMBLE 25
+#define RH_RF24_GPIO_SYNC_DETECTED 26
+#define RH_RF24_GPIO_RSSI_ABOVE_CAT 27
+#define RH_RF24_GPIO_TX_STATE 32
+#define RH_RF24_GPIO_RX_STATE 33
+#define RH_RF24_GPIO_RX_FIFO_ALMOST_FULL 34
+#define RH_RF24_GPIO_TX_FIFO_ALMOST_EMPTY 35
+#define RH_RF24_GPIO_BATT_LOW 36
+#define RH_RF24_GPIO_RSSI_ABOVE_CAT_LOW 37
+#define RH_RF24_GPIO_HOP 38
+#define RH_RF24_GPIO_HOP_TABLE_WRAPPED 39
+
+// #define RH_RF24_CMD_GET_INT_STATUS 0x20
+#define RH_RF24_INT_STATUS_CHIP_INT_STATUS 0x04
+#define RH_RF24_INT_STATUS_MODEM_INT_STATUS 0x02
+#define RH_RF24_INT_STATUS_PH_INT_STATUS 0x01
+#define RH_RF24_INT_STATUS_FILTER_MATCH 0x80
+#define RH_RF24_INT_STATUS_FILTER_MISS 0x40
+#define RH_RF24_INT_STATUS_PACKET_SENT 0x20
+#define RH_RF24_INT_STATUS_PACKET_RX 0x10
+#define RH_RF24_INT_STATUS_CRC_ERROR 0x08
+#define RH_RF24_INT_STATUS_TX_FIFO_ALMOST_EMPTY 0x02
+#define RH_RF24_INT_STATUS_RX_FIFO_ALMOST_FULL 0x01
+#define RH_RF24_INT_STATUS_INVALID_SYNC 0x20
+#define RH_RF24_INT_STATUS_RSSI_JUMP 0x10
+#define RH_RF24_INT_STATUS_RSSI 0x08
+#define RH_RF24_INT_STATUS_INVALID_PREAMBLE 0x04
+#define RH_RF24_INT_STATUS_PREAMBLE_DETECT 0x02
+#define RH_RF24_INT_STATUS_SYNC_DETECT 0x01
+#define RH_RF24_INT_STATUS_CAL 0x40
+#define RH_RF24_INT_STATUS_FIFO_UNDERFLOW_OVERFLOW_ERROR 0x20
+#define RH_RF24_INT_STATUS_STATE_CHANGE 0x10
+#define RH_RF24_INT_STATUS_CMD_ERROR 0x08
+#define RH_RF24_INT_STATUS_CHIP_READY 0x04
+#define RH_RF24_INT_STATUS_LOW_BATT 0x02
+#define RH_RF24_INT_STATUS_WUT 0x01
+
+//#define RH_RF24_PROPERTY_FRR_CTL_A_MODE 0x0200
+//#define RH_RF24_PROPERTY_FRR_CTL_B_MODE 0x0201
+//#define RH_RF24_PROPERTY_FRR_CTL_C_MODE 0x0202
+//#define RH_RF24_PROPERTY_FRR_CTL_D_MODE 0x0203
+#define RH_RF24_FRR_MODE_DISABLED 0
+#define RH_RF24_FRR_MODE_GLOBAL_STATUS 1
+#define RH_RF24_FRR_MODE_GLOBAL_INTERRUPT_PENDING 2
+#define RH_RF24_FRR_MODE_PACKET_HANDLER_STATUS 3
+#define RH_RF24_FRR_MODE_PACKET_HANDLER_INTERRUPT_PENDING 4
+#define RH_RF24_FRR_MODE_MODEM_STATUS 5
+#define RH_RF24_FRR_MODE_MODEM_INTERRUPT_PENDING 6
+#define RH_RF24_FRR_MODE_CHIP_STATUS 7
+#define RH_RF24_FRR_MODE_CHIP_INTERRUPT_PENDING 8
+#define RH_RF24_FRR_MODE_CURRENT_STATE 9
+#define RH_RF24_FRR_MODE_LATCHED_RSSI 10
+
+//#define RH_RF24_PROPERTY_INT_CTL_ENABLE 0x0100
+#define RH_RF24_CHIP_INT_STATUS_EN 0x04
+#define RH_RF24_MODEM_INT_STATUS_EN 0x02
+#define RH_RF24_PH_INT_STATUS_EN 0x01
+
+//#define RH_RF24_PROPERTY_PREAMBLE_CONFIG 0x1004
+#define RH_RF24_PREAMBLE_FIRST_1 0x20
+#define RH_RF24_PREAMBLE_FIRST_0 0x00
+#define RH_RF24_PREAMBLE_LENGTH_NIBBLES 0x00
+#define RH_RF24_PREAMBLE_LENGTH_BYTES 0x10
+#define RH_RF24_PREAMBLE_MAN_CONST 0x08
+#define RH_RF24_PREAMBLE_MAN_ENABLE 0x02
+#define RH_RF24_PREAMBLE_NON_STANDARD 0x00
+#define RH_RF24_PREAMBLE_STANDARD_1010 0x01
+#define RH_RF24_PREAMBLE_STANDARD_0101 0x02
+
+//#define RH_RF24_PROPERTY_SYNC_CONFIG 0x1100
+#define RH_RF24_SYNC_CONFIG_SKIP_TX 0x80
+#define RH_RF24_SYNC_CONFIG_RX_ERRORS_MASK 0x70
+#define RH_RF24_SYNC_CONFIG_4FSK 0x08
+#define RH_RF24_SYNC_CONFIG_MANCH 0x04
+#define RH_RF24_SYNC_CONFIG_LENGTH_MASK 0x03
+
+//#define RH_RF24_PROPERTY_PKT_CRC_CONFIG 0x1200
+#define RH_RF24_CRC_SEED_ALL_0S 0x00
+#define RH_RF24_CRC_SEED_ALL_1S 0x80
+#define RH_RF24_CRC_MASK 0x0f
+#define RH_RF24_CRC_NONE 0x00
+#define RH_RF24_CRC_ITU_T 0x01
+#define RH_RF24_CRC_IEC_16 0x02
+#define RH_RF24_CRC_BIACHEVA 0x03
+#define RH_RF24_CRC_16_IBM 0x04
+#define RH_RF24_CRC_CCITT 0x05
+#define RH_RF24_CRC_KOOPMAN 0x06
+#define RH_RF24_CRC_IEEE_802_3 0x07
+#define RH_RF24_CRC_CASTAGNOLI 0x08
+
+//#define RH_RF24_PROPERTY_PKT_CONFIG1 0x1206
+#define RH_RF24_PH_FIELD_SPLIT 0x80
+#define RH_RF24_PH_RX_DISABLE 0x40
+#define RH_RF24_4FSK_EN 0x20
+#define RH_RF24_RX_MULTI_PKT 0x10
+#define RH_RF24_MANCH_POL 0x08
+#define RH_RF24_CRC_INVERT 0x04
+#define RH_RF24_CRC_ENDIAN 0x02
+#define RH_RF24_BIT_ORDER 0x01
+
+//#define RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG 0x120f
+//#define RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG 0x1213
+//#define RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG 0x1217
+//#define RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG 0x121b
+//#define RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG 0x121f
+#define RH_RF24_FIELD_CONFIG_4FSK 0x10
+#define RH_RF24_FIELD_CONFIG_WHITEN 0x02
+#define RH_RF24_FIELD_CONFIG_MANCH 0x01
+
+//#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG 0x1224
+//#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG 0x1228
+//#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG 0x122c
+//#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG 0x1230
+//#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG 0x1234
+#define RH_RF24_FIELD_CONFIG_CRC_START 0x80
+#define RH_RF24_FIELD_CONFIG_SEND_CRC 0x20
+#define RH_RF24_FIELD_CONFIG_CHECK_CRC 0x08
+#define RH_RF24_FIELD_CONFIG_CRC_ENABLE 0x02
+
+
+
+
+//#define RH_RF24_PROPERTY_MODEM_MOD_TYPE 0x2000
+#define RH_RF24_TX_DIRECT_MODE_TYPE_SYNCHRONOUS 0x00
+#define RH_RF24_TX_DIRECT_MODE_TYPE_ASYNCHRONOUS 0x80
+#define RH_RF24_TX_DIRECT_MODE_GPIO0 0x00
+#define RH_RF24_TX_DIRECT_MODE_GPIO1 0x20
+#define RH_RF24_TX_DIRECT_MODE_GPIO2 0x40
+#define RH_RF24_TX_DIRECT_MODE_GPIO3 0x60
+#define RH_RF24_MOD_SOURCE_PACKET_HANDLER 0x00
+#define RH_RF24_MOD_SOURCE_DIRECT_MODE 0x08
+#define RH_RF24_MOD_SOURCE_RANDOM_GENERATOR 0x10
+#define RH_RF24_MOD_TYPE_CW 0x00
+#define RH_RF24_MOD_TYPE_OOK 0x01
+#define RH_RF24_MOD_TYPE_2FSK 0x02
+#define RH_RF24_MOD_TYPE_2GFSK 0x03
+#define RH_RF24_MOD_TYPE_4FSK 0x04
+#define RH_RF24_MOD_TYPE_4GFSK 0x05
+
+// RH_RF24_PROPERTY_PA_MODE 0x2200
+#define RH_RF24_PA_MODE_1_GROUP 0x04
+#define RH_RF24_PA_MODE_2_GROUPS 0x08
+#define RH_RF24_PA_MODE_CLASS_E 0x00
+#define RH_RF24_PA_MODE_SWITCH_CURRENT 0x01
+
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_RF24 RH_RF24.h <RH_RF24.h>
+/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF24 and compatible radio transceiver.
+///
+/// Works with
+/// - Silicon Labs Si4460/1/2/3/4 transceiver chips
+/// - The equivalent HopeRF RF24/25/26/27 transceiver chips
+/// - HopeRF Complete modules: RFM24W/26W/27W
+///
+/// \par Overview
+///
+/// This class provides basic functions for sending and receiving unaddressed,
+/// unreliable datagrams of arbitrary length to 250 octets per packet.
+///
+/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
+/// mesh routers, repeaters, translators etc.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// modulation scheme.
+///
+/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF
+/// RF24 and compatible radio modules, such as the RFM24W module.
+///
+/// The Hope-RF (http://www.hoperf.com) RF24 family is a low-cost ISM transceiver
+/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and
+/// programmable data rates. HopeRF also sell these chips on modules which includes
+/// a crystal and antenna coupling circuits: RFM24W, RFM26W and RFM27W
+///
+/// This Driver provides functions for sending and receiving messages of up
+/// to 250 octets on any frequency supported by the RF24, in a range of
+/// predefined data rates and frequency deviations. Frequency can be set
+/// to any frequency from 142.0MHz to 1050.0MHz. Caution: most modules only support a more limited
+/// range of frequencies due to antenna tuning.
+///
+/// Up to 2 RFM24 modules can be connected to an Arduino (3 on a Mega),
+/// permitting the construction of translators and frequency changers, etc.
+///
+/// The following modulation types are suppported with a range of modem configurations for
+/// common data rates and frequency deviations:
+/// - OOK On-Off Keying
+/// - GFSK Gaussian Frequency Shift Keying
+/// - FSK Frequency Shift Keying
+///
+/// Support for other RF24 features such as on-chip temperature measurement,
+/// transmitter power control etc is also provided.
+///
+/// RH_RF24 uses interrupts to detect and handle events in the radio chip. The RF24 family has
+/// TX and RX FIFOs of 64 bytes, but through the use of interrupt, the RH_RF24 driver can send longer
+/// messages by filling or emptying the FIFOs on-the-fly.
+///
+/// Tested on Anarduino Mini http://www.anarduino.com/mini/ with arduino-1.0.5
+/// on OpenSuSE 13.1
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this RH_RF24 Driver conform to this packet format:
+///
+/// - 4 octets PREAMBLE (configurable)
+/// - 2 octets SYNC 0x2d, 0xd4 (configurable, so you can use this as a network filter)
+/// - Field containing 1 octet of message length and 2 octet CRC protecting this field
+/// - Field 2 containing at least 4 octets, and 2 octet CRC protecting this field:
+/// + 4 octets HEADER: (TO, FROM, ID, FLAGS)
+/// + 0 to 250 octets DATA
+/// + 2 octets CRC, computed on HEADER and DATA
+///
+/// \par Connecting RFM-24 to Arduino
+///
+/// For RFM24/RFM26 and Teensy 3.1 or Anarduino Mini
+/// \code
+/// Teensy RFM-24/RFM26
+/// GND----------GND (ground in)
+/// 3V3----------VCC (3.3V in)
+/// interrupt 2 pin D2-----------NIRQ (interrupt request out)
+/// SS pin D10----------NSEL (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------SDI (SPI Data in)
+/// MISO pin D12----------SDO (SPI data out)
+/// D9-----------SDN (shutdown in)
+/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT)
+/// \--TX_ANT (TX antenna control in) RFM22B only
+/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT)
+/// \--RX_ANT (RX antenna control in) RFM22B only
+/// \endcode
+/// Caution: tying the radio SDN pin to ground (though it might appear from the data sheets to make sense)
+/// does not always produce a reliable radio startup. So this driver controls the SDN pin directly.
+/// Note: the GPIO0-TX_ANT and GPIO1-RX_ANT connections are not required for the 11dBm RFM24W,
+/// which has no antenna switch.
+///
+/// \par Customising
+///
+/// The library will work out of the box with the provided examples, over the full frequency range and with
+/// a wide range of predefined modem configurations schemes and speeds. However, you may want to
+/// change the default behaviour of this library. There are several ways you can do this:
+///
+/// - Use the RH_RF24 API based on this documentation
+/// - Create your own ModemConfig and pass it to setModemreeegisters()
+/// - Generate a new radio_config_Si4460.h using the Silicon Labs WDS software package
+/// - Write directly to the radio registers and properties using command() and set_properties()
+///
+/// \par RSSI
+///
+/// The RSSI (Received Signal Strength Indicator) is measured and latched after the message sync bytes are received.
+/// The latched RSSI is available from the lastRssi() member functionafter the complete message is received.
+/// Although lastRssi()
+/// supposedly returns a signed integer, in the case of this radio it actually returns an unsigned 8 bit integer (uint8_t)
+/// and you will have to cast the return value to use it:
+/// \code
+/// uint8_t lastRssi = (uint8_t)rf24.lastRssi();
+/// \endcode
+/// The units of RSSI are arbitrary and relative, with larger unsigned numbers indicating a stronger signal. Values up to 255
+/// are seen with radios in close proximity to each other. Lower limit of receivable strength is about 70.
+///
+/// \par Transmitter Power
+///
+/// You can control the transmitter power on the RF24/25/26/27 transceiver
+/// with the RH_RF24::setTxPower() function. The argument can be any of
+/// 0x00 to 0x4f (for RFM24/Si4460) or
+/// 0x00 to 0x7f (for others)
+/// 0x00 will yield no measurable power. For other settings there is a non-linear correlation with actual
+/// RF power output (see below)
+/// The default is 0x10. Eg:
+/// \code
+/// driver.setTxPower(0x10);
+/// \endcode
+///
+/// We have made some actual power measurements against
+/// programmed power
+/// - Anarduino Mini with RFM24-433 and RFM26-433 at Vcc = 3.3V, in CW mode, 434MHz
+/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND
+/// - bnc connecteor
+/// - 12dB attenuator
+/// - BNC-SMA adapter
+/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
+/// - Digitech QM-1460 digital multimeter
+/// \code
+/// Program power Measured Power dBm
+/// HEX RFM24 RFM26
+/// 0x00 not measurable not measurable
+/// 0x01 -20.4 -20.6
+/// 0x0f 2.4 4.8
+/// 0x1f 9.4 11.0
+/// 0x2f 11.2 14.2
+/// 0x3f 11.6 16.4
+/// 0x4f 11.6 18.0
+/// 0x5f 18.6
+/// 0x6f 19.0
+/// 0x7f 19.2
+/// \endcode
+/// Caution: the actual radiated power output will depend heavily on the power supply voltage and the antenna.
+
+class RH_RF24 : public RHSPIDriver
+{
+public:
+ /// \brief Defines property values for a set of modem configuration registers
+ ///
+ /// Defines property values for a set of modem configuration registers
+ /// that can be passed to setModemRegisters() if none of the choices in
+ /// ModemConfigChoice suit your need setModemRegisters() writes the
+ /// property values from this structure to the appropriate RF24 properties
+ /// to set the desired modulation type, data rate and deviation/bandwidth.
+ typedef struct
+ {
+ uint8_t prop_2000; ///< Value for property RH_RF24_PROPERTY_MODEM_MOD_TYPE
+ uint8_t prop_2003; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_2
+ uint8_t prop_2004; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_1
+ uint8_t prop_2005; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_0
+ uint8_t prop_2006; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3
+ uint8_t prop_200a; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_2
+ uint8_t prop_200b; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_1
+ uint8_t prop_200c; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_0
+ uint8_t prop_2018; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY
+ uint8_t prop_201e; ///< Value for property RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1
+ uint8_t prop_201f; ///< Value for property RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0
+ uint8_t prop_2022; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_OSR_1
+ uint8_t prop_2023; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_OSR_0
+ uint8_t prop_2024; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2
+ uint8_t prop_2025; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1
+ uint8_t prop_2026; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0
+ uint8_t prop_2027; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GAIN_1
+ uint8_t prop_2028; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GAIN_0
+ uint8_t prop_2029; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GEAR
+ uint8_t prop_202d; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_WAIT
+ uint8_t prop_202e; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_GAIN_1
+ uint8_t prop_202f; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_GAIN_0
+ uint8_t prop_2030; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1
+ uint8_t prop_2031; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0
+ uint8_t prop_2035; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_CONTROL
+ uint8_t prop_2038; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE
+ uint8_t prop_2039; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY
+ uint8_t prop_203a; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY
+ uint8_t prop_203b; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_GAIN1
+ uint8_t prop_203c; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_GAIN0
+ uint8_t prop_203d; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_TH1
+ uint8_t prop_203e; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_TH0
+ uint8_t prop_203f; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_MAP
+ uint8_t prop_2040; ///< Value for property RH_RF24_PROPERTY_MODEM_OOK_PDTC
+ uint8_t prop_2043; ///< Value for property RH_RF24_PROPERTY_MODEM_OOK_MISC
+ uint8_t prop_2045; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_CONTROL
+ uint8_t prop_2046; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_EYE_1
+ uint8_t prop_2047; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_EYE_0
+ uint8_t prop_204e; ///< Value for property RH_RF24_PROPERTY_MODEM_RSSI_COMP
+ uint8_t prop_2100; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0
+ uint8_t prop_2101; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0
+ uint8_t prop_2102; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0
+ uint8_t prop_2103; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0
+ uint8_t prop_2104; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0
+ uint8_t prop_2105; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0
+ uint8_t prop_2106; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0
+ uint8_t prop_2107; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0
+ uint8_t prop_2108; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0
+ uint8_t prop_2109; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0
+ uint8_t prop_210a; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0
+ uint8_t prop_210b; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0
+ uint8_t prop_210c; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0
+ uint8_t prop_210d; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0
+ uint8_t prop_210e; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0
+ uint8_t prop_210f; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1
+ uint8_t prop_2110; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2
+ uint8_t prop_2111; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3
+ uint8_t prop_2112; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0
+ uint8_t prop_2113; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0
+ uint8_t prop_2114; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0
+ uint8_t prop_2115; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0
+ uint8_t prop_2116; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0
+ uint8_t prop_2117; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0
+ uint8_t prop_2118; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0
+ uint8_t prop_2119; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0
+ uint8_t prop_211a; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0
+ uint8_t prop_211b; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0
+ uint8_t prop_211c; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0
+ uint8_t prop_211d; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0
+ uint8_t prop_211e; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0
+ uint8_t prop_211f; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0
+ uint8_t prop_2120; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0
+ uint8_t prop_2121; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1
+ uint8_t prop_2122; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2
+ uint8_t prop_2123; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3
+ uint8_t prop_2203; ///< Value for property RH_RF24_PROPERTY_PA_TC
+ uint8_t prop_2300; ///< Value for property RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF
+ uint8_t prop_2301; ///< Value for property RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT
+ uint8_t prop_2303; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT3
+ uint8_t prop_2304; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT2
+ uint8_t prop_2305; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT1
+ } ModemConfig;
+
+ /// Choices for setModemConfig() for a selected subset of common
+ /// modulation types, and data rates. If you need another configuration,
+ /// use the register calculator. and call setModemRegisters() with your
+ /// desired settings.
+ /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic
+ /// definitions and not their integer equivalents: its possible that values will be
+ /// changed in later versions (though we will try to avoid it).
+ /// Contributions of new complete and tested ModemConfigs ready to add to this list will be readily accepted.
+ typedef enum
+ {
+ FSK_Rb0_5Fd1 = 0, ///< FSK Rb = 0.5kbs, Fd = 1kHz
+ FSK_Rb5Fd10, ///< FSK Rb = 5kbs, Fd = 10kHz
+ FSK_Rb50Fd100, ///< FSK Rb = 50kbs, Fd = 100kHz
+ FSK_Rb150Fd300, ///< FSK Rb = 50kbs, Fd = 100kHz
+
+ GFSK_Rb0_5Fd1, ///< GFSK Rb = 0.5kbs, Fd = 1kHz
+ GFSK_Rb5Fd10, ///< GFSK Rb = 5kbs, Fd = 10kHz
+ GFSK_Rb50Fd100, ///< GFSK Rb = 50kbs, Fd = 100kHz
+ GFSK_Rb150Fd300, ///< GFSK Rb = 150kbs, Fd = 300kHz
+
+ // We were unable to get any other OOKs to work
+ OOK_Rb5Bw30, ///< OOK Rb = 5kbs, Bw = 30kHz
+ OOK_Rb10Bw40, ///< OOK Rb = 10kbs, Bw = 40kHz
+
+ // We were unable to get any 4FSK or 4GFSK schemes to work
+
+ } ModemConfigChoice;
+
+ /// \brief Defines the available choices for CRC
+ /// Types of permitted CRC polynomials, to be passed to setCRCPolynomial()
+ /// They deliberately have the same numeric values as the CRC_POLYNOMIAL field of PKT_CRC_CONFIG
+ typedef enum
+ {
+ CRC_NONE = 0,
+ CRC_ITU_T,
+ CRC_IEC_16,
+ CRC_Biacheva,
+ CRC_16_IBM,
+ CRC_CCITT,
+ CRC_Koopman,
+ CRC_IEEE_802_3,
+ CRC_Castagnoli,
+ } CRCPolynomial;
+
+ /// \brief Defines the commands we can interrogate in printRegisters
+ typedef struct
+ {
+ uint8_t cmd; ///< The command number
+ uint8_t replyLen; ///< Number of bytes in the reply stream (after the CTS)
+ } CommandInfo;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface
+ /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient
+ /// distinct interrupt lines, one for each instance.
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RF24 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
+ /// \param[in] interruptPin The interrupt Pin number that is connected to the RF24 DIO0 interrupt line.
+ /// Defaults to pin 2.
+ /// Caution: You must specify an interrupt capable pin.
+ /// On many Arduino boards, there are limitations as to which pins may be used as interrupts.
+ /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin.
+ /// On other Arduinos pins 2 or 3.
+ /// See http://arduino.cc/en/Reference/attachInterrupt for more details.
+ /// On Chipkit Uno32, pins 38, 2, 7, 8, 35.
+ /// On other boards, any digital pin may be used.
+ /// \param [in] sdnPin The pin number connected to SDN on the radio. Defaults to pin 9.
+ /// Connecting SDN directly to ground does not aloways provide reliable radio startup.
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_RF24(PINS slaveSelectPin, PINS interruptPin, PINS sdnPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:
+ /// - Initialise the slave select and shutdown pins and the SPI interface library
+ /// - Checks the connected RF24 module can be communicated
+ /// - Attaches an interrupt handler
+ /// - Configures the RF24 module
+ /// - Sets the frequency to 434.0 MHz
+ /// - Sets the modem data rate to GFSK_Rb5Fd10
+ /// - Sets the tranmitter power level to 16 (about 2.4dBm on RFM4)
+ /// \return true if everything was successful
+ bool init();
+
+ /// Sets the chip mode that will be used when the RH_RF24 driver is idle (ie not transmitting or receiving)
+ /// You can use this to control the power level consumed while idle, at the cost of slower
+ /// transition to tranmit or receive states
+ /// \param[in] idleMode The chip state to use when idle. Sensible choices might be RH_RF24_DEVICE_STATE_SLEEP or RH_RF24_DEVICE_STATE_READY
+ void setIdleMode(uint8_t idleMode);
+
+ /// Sets the transmitter and receiver
+ /// centre frequency.
+ /// Valid frequency ranges for RFM24/Si4460, Si4461, RFM25/Si4463 are:
+ /// 142MHz to 175Mhz, 284MHz to 350MHz, 425MHz to 525MHz, 850MHz to 1050MHz.
+ /// Valid frequency ranges for RFM26/Si4464 are:
+ /// 119MHz to 960MHz.
+ /// Caution: RFM modules are designed with antenna coupling components to suit a limited band
+ /// of frequencies (marked underneath the module). It is possible to set frequencies in other bands,
+ /// but you may only get little or no power radiated.
+ /// \param[in] centre Frequency in MHz.
+ /// \param[in] afcPullInRange Not used
+ /// \return true if the selected frequency is within a valid range for the connected radio and if
+ /// setting the new frequency succeeded.
+ bool setFrequency(float centre, float afcPullInRange = 0.05);
+
+ /// Sets all the properties required to configure the data modem in the RF24, including the data rate,
+ /// bandwidths etc. You can use this to configure the modem with custom configurations if none of the
+ /// canned configurations in ModemConfigChoice suit you.
+ /// \param[in] config A ModemConfig structure containing values for the modem configuration registers.
+ void setModemRegisters(const ModemConfig* config);
+
+ /// Select one of the predefined modem configurations. If you need a modem configuration not provided
+ /// here, use setModemRegisters() with your own ModemConfig. The default after init() is RH_RF24::GFSK_Rb5Fd10.
+ /// \param[in] index The configuration choice.
+ /// \return true if index is a valid choice.
+ bool setModemConfig(ModemConfigChoice index);
+
+ /// Starts the receiver and checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is NOT permitted.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+ /// Sets the length of the preamble
+ /// in bytes.
+ /// Caution: this should be set to the same
+ /// value on all nodes in your network. Default is 4.
+ /// \param[in] bytes Preamble length in bytes.
+ void setPreambleLength(uint16_t bytes);
+
+ /// Sets the sync words for transmit and receive
+ /// Caution: SyncWords should be set to the same
+ /// value on all nodes in your network. Nodes with different SyncWords set will never receive
+ /// each others messages, so different SyncWords can be used to isolate different
+ /// networks from each other. Default is { 0x2d, 0xd4 }.
+ /// \param[in] syncWords Array of sync words, 1 to 4 octets long. NULL if no sync words to be used.
+ /// \param[in] len Number of sync words to set, 1 to 4. 0 if no sync words to be used.
+ void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0);
+
+ /// Sets the CRC polynomial to be used to generate the CRC for both receive and transmit
+ /// otherwise the default of CRC_16_IBM will be used.
+ /// \param[in] polynomial One of RH_RF24::CRCPolynomial choices CRC_*
+ /// \return true if polynomial is a valid option for this radio.
+ bool setCRCPolynomial(CRCPolynomial polynomial);
+
+ /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running,
+ /// disables them.
+ void setModeIdle();
+
+ /// If current mode is Tx or Idle, changes it to Rx.
+ /// Starts the receiver in the RF24.
+ void setModeRx();
+
+ /// If current mode is Rx or Idle, changes it to Rx. F
+ /// Starts the transmitter in the RF24.
+ void setModeTx();
+
+ /// Sets the transmitter power output level register PA_PWR_LVL
+ /// The power argument to this function has a non-linear correlation with the actual RF power output.
+ /// See the transmitter power table above for some examples.
+ /// Also the Si446x Data Sheet section 5.4.2 may be helpful.
+ /// Be a good neighbour and set the lowest power level you need.
+ /// Caution: legal power limits may apply in certain countries.
+ /// After init(), the power will be set to 0x10.
+ /// \param[in] power Transmitter power level. For RFM24/Si4460, valid values are 0x00 to 0x4f. For others, 0x00 to 0x7f
+ void setTxPower(uint8_t power);
+
+ /// Dump the values of available command replies and properties
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// Not all commands have valid replies, therefore they are not all printed.
+ /// Caution: the list is very long
+ bool printRegisters();
+
+ /// Send a string of command bytes to the chip and get a string of reply bytes
+ /// Different RFM24 commands take different numbers of command bytes and send back different numbers
+ /// of reply bytes. See the Si446x documentaiton for more details.
+ /// Both command bytes and reply bytes are optional
+ /// \param[in] cmd The command number. One of RH_RF24_CMD_*
+ /// \param[in] write_buf Pointer to write_len bytes of command input bytes to send. If there are none, set to NULL.
+ /// \param[in] write_len The number of bytes to send from write_buf. If there are none, set to 0
+ /// \param[out] read_buf Pointer to read_len bytes of storage where the reply stream from the comand will be written.
+ /// If none are required, set to NULL
+ /// \param[in] read_len The number of bytes to read from the reply stream. If none required, set to 0.
+ /// \return true if the command succeeeded.
+ bool command(uint8_t cmd, const uint8_t* write_buf = 0, uint8_t write_len = 0, uint8_t* read_buf = 0, uint8_t read_len = 0);
+
+ /// Set one or more chip properties using the RH_RF24_CMD_SET_PROPERTY
+ /// command. See the Si446x API Description AN625 for details on what properties are available.
+ /// param[in] firstProperty The property number of the first property to set. The first value in the values array
+ /// will be used to set this property, and any subsequent values will be used to set the following properties.
+ /// One of RH_RF24_PROPERTY_*
+ /// param[in] values Array of 0 or more values to write the firstProperty and subsequent proerties
+ /// param[in] count The number of values in the values array
+ /// \return true if the command succeeeded.
+ bool set_properties(uint16_t firstProperty, const uint8_t* values, uint8_t count);
+
+ /// Get one or more chip properties using the RH_RF24_CMD_GET_PROPERTY
+ /// command. See the Si446x API Description AN625 for details on what properties are available.
+ /// param[in] firstProperty The property number of the first property to get. The first value in the values array
+ /// will be set with this property, and any subsequent values will be set from the following properties.
+ /// One of RH_RF24_PROPERTY_*
+ /// param[out] values Array of 0 or more values to receive the firstProperty and subsequent proerties
+ /// param[in] count The number of values in the values array
+ /// \return true if the command succeeeded.
+ bool get_properties(uint16_t firstProperty, uint8_t* values, uint8_t count);
+
+ /// Measures and returns the current
+ /// Chip temperature.
+ /// \return The current chip temperature in degrees Centigrade
+ float get_temperature();
+
+ /// Measures and returns the current
+ /// Chip Vcc supply voltage.
+ /// \return The current chip Vcc supply voltage in Volts.
+ float get_battery_voltage();
+
+ /// Measures and returns the current
+ /// voltage applied to a GPIO pin (which has previously been configured as a voltage input)
+ /// \param[in] gpio The GPIO pin to read. 0 to 3.
+ /// \return The current pin voltage in Volts.
+ float get_gpio_voltage(uint8_t gpio);
+
+ /// Read one of the Fast Read Response registers.
+ /// The Fast Read Response register must be previously configured with the matching
+ /// RH_RF24_PROPERTY_FRR_CTL_?_MODE property to select what chip property will be available in that register.
+ /// \param[in] reg The index of the FRR register to read. 0 means FRR A, 1 means B etc.
+ /// \return the value read from the specified Fast Read Response register.
+ uint8_t frr_read(uint8_t reg);
+
+ /// Sets the radio into low-power sleep mode.
+ /// If successful, the transport will stay in sleep mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// Caution: there is a time penalty as the radio takes a finte time to wake from sleep mode.
+ /// \return true if sleep mode was successfully entered.
+ virtual bool sleep();
+
+protected:
+ /// This is a low level function to handle the interrupts for one instance of RF24.
+ /// Called automatically by isr*()
+ /// Should not need to be called by user code.
+ void handleInterrupt();
+
+ /// Clears the chips RX FIFO
+ /// \return true if successful
+ bool clearRxFifo();
+
+ /// Clears RH_RF24's internal TX and RX buffers and counters
+ void clearBuffer();
+
+ /// Loads the next part of the currently transmitting message
+ /// into the chips TX buffer
+ void sendNextFragment();
+
+ /// Copies the next part of the currenrtly received message from the chips RX FIFO to the
+ /// receive buffer
+ void readNextFragment();
+
+ /// Loads data into the chips TX FIFO
+ /// \param[in] data Array of data bytes to be loaded
+ /// \param[in] len Number of bytes in data to be loaded
+ /// \return true if successful
+ bool writeTxFifo(uint8_t *data, uint8_t len);
+
+ /// Checks the contents of the RX buffer.
+ /// If it contans a valid message adressed to this node
+ /// sets _rxBufValid.
+ void validateRxBuf();
+
+ /// Cycles the Shutdown pin to force the cradio chip to reset
+ void power_on_reset();
+
+ /// Sets registers, commands and properties
+ /// in the ratio according to the data in the commands array
+ /// \param[in] commands Array of data containing radio commands in the format provided by radio_config_Si4460.h
+ /// \return true if successful
+ bool configure(const uint8_t* commands);
+
+ /// Clears all pending interrutps in the radio chip.
+ bool cmd_clear_all_interrupts();
+
+private:
+
+ /// Low level interrupt service routine for RF24 connected to interrupt 0
+ static void isr0();
+
+ /// Low level interrupt service routine for RF24 connected to interrupt 1
+ static void isr1();
+
+ /// Low level interrupt service routine for RF24 connected to interrupt 1
+ static void isr2();
+
+ /// Array of instances connected to interrupts 0 and 1
+ static RH_RF24* _deviceForInterrupt[];
+
+ /// Index of next interrupt number to use in _deviceForInterrupt
+ static uint8_t _interruptCount;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ /// The configured interrupt pin connected to this instance
+ InterruptIn _interruptPin;
+#else
+ /// The configured interrupt pin connected to this instance
+ uint8_t _interruptPin;
+#endif
+
+ /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
+ /// else 0xff
+ uint8_t _myInterruptIndex;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ /// The configured pin connected to the SDN pin of the radio
+ DigitalOut _sdnPin;
+#else
+ /// The configured pin connected to the SDN pin of the radio
+ uint8_t _sdnPin;
+#endif
+
+ /// The radio OP mode to use when mode is RHModeIdle
+ uint8_t _idleMode;
+
+ /// The reported PART device type
+ uint16_t _deviceType;
+
+ /// The selected output power in dBm
+ int8_t _power;
+
+ /// The message length in _buf
+ volatile uint8_t _bufLen;
+
+ /// Array of octets of the last received message or the next to transmit message
+ uint8_t _buf[RH_RF24_MAX_PAYLOAD_LEN];
+
+ /// True when there is a valid message in the Rx buffer
+ volatile bool _rxBufValid;
+
+ /// Index into TX buffer of the next to send chunk
+ volatile uint8_t _txBufSentIndex;
+
+ /// Time in millis since the last preamble was received (and the last time the RSSI was measured)
+ uint32_t _lastPreambleTime;
+
+};
+
+/// @example rf24_client.pde
+/// @example rf24_server.pde
+/// @example rf24_reliable_datagram_client.pde
+/// @example rf24_reliable_datagram_server.pde
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF69.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,562 @@
+// RH_RF69.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF69.cpp,v 1.25 2015/05/17 00:11:26 mikem Exp $
+
+#include <RH_RF69.h>
+
+// Interrupt vectors for the 3 Arduino interrupt pins
+// Each interrupt can be handled by a different instance of RH_RF69, allowing you to have
+// 2 or more RF69s per Arduino
+RH_RF69* RH_RF69::_deviceForInterrupt[RH_RF69_NUM_INTERRUPTS] = {0, 0, 0};
+uint8_t RH_RF69::_interruptCount = 0; // Index into _deviceForInterrupt for next device
+
+// These are indexed by the values of ModemConfigChoice
+// Stored in flash (program) memory to save SRAM
+// It is important to keep the modulation index for FSK between 0.5 and 10
+// modulation index = 2 * Fdev / BR
+// Note that I have not had much success with FSK with Fd > ~5
+// You have to construct these by hand, using the data from the RF69 Datasheet :-(
+// or use the SX1231 starter kit software (Ctl-Alt-N to use that without a connected radio)
+#define CONFIG_FSK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_FSK | RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_NONE)
+#define CONFIG_GFSK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_FSK | RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT1_0)
+#define CONFIG_OOK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_OOK | RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_NONE)
+
+// Choices for RH_RF69_REG_37_PACKETCONFIG1:
+#define CONFIG_NOWHITE (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_NONE | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
+#define CONFIG_WHITE (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_WHITENING | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
+#define CONFIG_MANCHESTER (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_MANCHESTER | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
+PROGMEM static const RH_RF69::ModemConfig MODEM_CONFIG_TABLE[] =
+{
+ // 02, 03, 04, 05, 06, 19, 1a, 37
+ // FSK, No Manchester, no shaping, whitening, CRC, no address filtering
+ // AFC BW == RX BW == 2 x bit rate
+ // Low modulation indexes of ~ 1 at slow speeds do not seem to work very well. Choose MI of 2.
+ { CONFIG_FSK, 0x3e, 0x80, 0x00, 0x52, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb2Fd5
+ { CONFIG_FSK, 0x34, 0x15, 0x00, 0x4f, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb2_4Fd4_8
+ { CONFIG_FSK, 0x1a, 0x0b, 0x00, 0x9d, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb4_8Fd9_6
+
+ { CONFIG_FSK, 0x0d, 0x05, 0x01, 0x3b, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb9_6Fd19_2
+ { CONFIG_FSK, 0x06, 0x83, 0x02, 0x75, 0xf3, 0xf3, CONFIG_WHITE}, // FSK_Rb19_2Fd38_4
+ { CONFIG_FSK, 0x03, 0x41, 0x04, 0xea, 0xf2, 0xf2, CONFIG_WHITE}, // FSK_Rb38_4Fd76_8
+
+ { CONFIG_FSK, 0x02, 0x2c, 0x07, 0xae, 0xe2, 0xe2, CONFIG_WHITE}, // FSK_Rb57_6Fd120
+ { CONFIG_FSK, 0x01, 0x00, 0x08, 0x00, 0xe1, 0xe1, CONFIG_WHITE}, // FSK_Rb125Fd125
+ { CONFIG_FSK, 0x00, 0x80, 0x10, 0x00, 0xe0, 0xe0, CONFIG_WHITE}, // FSK_Rb250Fd250
+ { CONFIG_FSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x42, CONFIG_WHITE}, // FSK_Rb55555Fd50
+
+ // 02, 03, 04, 05, 06, 19, 1a, 37
+ // GFSK (BT=1.0), No Manchester, whitening, CRC, no address filtering
+ // AFC BW == RX BW == 2 x bit rate
+ { CONFIG_GFSK, 0x3e, 0x80, 0x00, 0x52, 0xf4, 0xf5, CONFIG_WHITE}, // GFSK_Rb2Fd5
+ { CONFIG_GFSK, 0x34, 0x15, 0x00, 0x4f, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb2_4Fd4_8
+ { CONFIG_GFSK, 0x1a, 0x0b, 0x00, 0x9d, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb4_8Fd9_6
+
+ { CONFIG_GFSK, 0x0d, 0x05, 0x01, 0x3b, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb9_6Fd19_2
+ { CONFIG_GFSK, 0x06, 0x83, 0x02, 0x75, 0xf3, 0xf3, CONFIG_WHITE}, // GFSK_Rb19_2Fd38_4
+ { CONFIG_GFSK, 0x03, 0x41, 0x04, 0xea, 0xf2, 0xf2, CONFIG_WHITE}, // GFSK_Rb38_4Fd76_8
+
+ { CONFIG_GFSK, 0x02, 0x2c, 0x07, 0xae, 0xe2, 0xe2, CONFIG_WHITE}, // GFSK_Rb57_6Fd120
+ { CONFIG_GFSK, 0x01, 0x00, 0x08, 0x00, 0xe1, 0xe1, CONFIG_WHITE}, // GFSK_Rb125Fd125
+ { CONFIG_GFSK, 0x00, 0x80, 0x10, 0x00, 0xe0, 0xe0, CONFIG_WHITE}, // GFSK_Rb250Fd250
+ { CONFIG_GFSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x42, CONFIG_WHITE}, // GFSK_Rb55555Fd50
+
+ // 02, 03, 04, 05, 06, 19, 1a, 37
+ // OOK, No Manchester, no shaping, whitening, CRC, no address filtering
+ // with the help of the SX1231 configuration program
+ // AFC BW == RX BW
+ // All OOK configs have the default:
+ // Threshold Type: Peak
+ // Peak Threshold Step: 0.5dB
+ // Peak threshiold dec: ONce per chip
+ // Fixed threshold: 6dB
+ { CONFIG_OOK, 0x7d, 0x00, 0x00, 0x10, 0x88, 0x88, CONFIG_WHITE}, // OOK_Rb1Bw1
+ { CONFIG_OOK, 0x68, 0x2b, 0x00, 0x10, 0xf1, 0xf1, CONFIG_WHITE}, // OOK_Rb1_2Bw75
+ { CONFIG_OOK, 0x34, 0x15, 0x00, 0x10, 0xf5, 0xf5, CONFIG_WHITE}, // OOK_Rb2_4Bw4_8
+ { CONFIG_OOK, 0x1a, 0x0b, 0x00, 0x10, 0xf4, 0xf4, CONFIG_WHITE}, // OOK_Rb4_8Bw9_6
+ { CONFIG_OOK, 0x0d, 0x05, 0x00, 0x10, 0xf3, 0xf3, CONFIG_WHITE}, // OOK_Rb9_6Bw19_2
+ { CONFIG_OOK, 0x06, 0x83, 0x00, 0x10, 0xf2, 0xf2, CONFIG_WHITE}, // OOK_Rb19_2Bw38_4
+ { CONFIG_OOK, 0x03, 0xe8, 0x00, 0x10, 0xe2, 0xe2, CONFIG_WHITE}, // OOK_Rb32Bw64
+
+// { CONFIG_FSK, 0x68, 0x2b, 0x00, 0x52, 0x55, 0x55, CONFIG_WHITE}, // works: Rb1200 Fd 5000 bw10000, DCC 400
+// { CONFIG_FSK, 0x0c, 0x80, 0x02, 0x8f, 0x52, 0x52, CONFIG_WHITE}, // works 10/40/80
+// { CONFIG_FSK, 0x0c, 0x80, 0x02, 0x8f, 0x53, 0x53, CONFIG_WHITE}, // works 10/40/40
+
+};
+RH_RF69::RH_RF69(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi)
+ :
+ RHSPIDriver(slaveSelectPin, spi),
+ _interruptPin(interruptPin)
+{
+ _idleMode = RH_RF69_OPMODE_MODE_STDBY;
+ _myInterruptIndex = 0xff; // Not allocated yet
+}
+
+void RH_RF69::setIdleMode(uint8_t idleMode)
+{
+ _idleMode = idleMode;
+}
+
+bool RH_RF69::init()
+{
+ if (!RHSPIDriver::init())
+ return false;
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Determine the interrupt number that corresponds to the interruptPin
+ int interruptNumber = digitalPinToInterrupt(_interruptPin);
+ if (interruptNumber == NOT_AN_INTERRUPT)
+ return false;
+#endif
+
+ // Get the device type and check it
+ // This also tests whether we are really connected to a device
+ // My test devices return 0x24
+ _deviceType = spiRead(RH_RF69_REG_10_VERSION);
+ if (_deviceType == 00 ||
+ _deviceType == 0xff)
+ return false;
+
+ // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
+ // ARM M4 requires the below. else pin interrupt doesn't work properly.
+ // On all other platforms, its innocuous, belt and braces
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ pinMode(_interruptPin, INPUT);
+#endif
+
+
+
+ // Set up interrupt handler
+ // Since there are a limited number of interrupt glue functions isr*() available,
+ // we can only support a limited number of devices simultaneously
+ // ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
+ // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
+ // yourself based on knwledge of what Arduino board you are running on.
+ if (_myInterruptIndex == 0xff)
+ {
+ // First run, no interrupt allocated yet
+ if (_interruptCount <= RH_RF69_NUM_INTERRUPTS)
+ _myInterruptIndex = _interruptCount++;
+ else
+ return false; // Too many devices, not enough interrupt vectors
+ }
+ _deviceForInterrupt[_myInterruptIndex] = this;
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ if (_myInterruptIndex == 0)
+ _interruptPin.rise(&isr0);
+ else if (_myInterruptIndex == 1)
+ _interruptPin.rise(&isr1);
+ else if (_myInterruptIndex == 2)
+ _interruptPin.rise(&isr2);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#else
+ if (_myInterruptIndex == 0)
+ attachInterrupt(interruptNumber, isr0, RISING);
+ else if (_myInterruptIndex == 1)
+ attachInterrupt(interruptNumber, isr1, RISING);
+ else if (_myInterruptIndex == 2)
+ attachInterrupt(interruptNumber, isr2, RISING);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#endif
+
+
+ setModeIdle();
+
+ // Configure important RH_RF69 registers
+ // Here we set up the standard packet format for use by the RH_RF69 library:
+ // 4 bytes preamble
+ // 2 SYNC words 2d, d4
+ // 2 CRC CCITT octets computed on the header, length and data (this in the modem config data)
+ // 0 to 60 bytes data
+ // RSSI Threshold -114dBm
+ // We dont use the RH_RF69s address filtering: instead we prepend our own headers to the beginning
+ // of the RH_RF69 payload
+ spiWrite(RH_RF69_REG_3C_FIFOTHRESH, RH_RF69_FIFOTHRESH_TXSTARTCONDITION_NOTEMPTY | 0x0f); // thresh 15 is default
+ // RSSITHRESH is default
+// spiWrite(RH_RF69_REG_29_RSSITHRESH, 220); // -110 dbM
+ // SYNCCONFIG is default. SyncSize is set later by setSyncWords()
+// spiWrite(RH_RF69_REG_2E_SYNCCONFIG, RH_RF69_SYNCCONFIG_SYNCON); // auto, tolerance 0
+ // PAYLOADLENGTH is default
+// spiWrite(RH_RF69_REG_38_PAYLOADLENGTH, RH_RF69_FIFO_SIZE); // max size only for RX
+ // PACKETCONFIG 2 is default
+ spiWrite(RH_RF69_REG_6F_TESTDAGC, RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAOFF);
+ // If high power boost set previously, disable it
+ spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
+ spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
+
+ // The following can be changed later by the user if necessary.
+ // Set up default configuration
+ uint8_t syncwords[] = { 0x2d, 0xd4 };
+ setSyncWords(syncwords, sizeof(syncwords)); // Same as RF22's
+ // Reasonably fast and reliable default speed and modulation
+ setModemConfig(GFSK_Rb250Fd250);
+
+ // 3 would be sufficient, but this is the same as RF22's
+ setPreambleLength(4);
+ // An innocuous ISM frequency, same as RF22's
+ setFrequency(434.0);
+ // No encryption
+ setEncryptionKey(NULL);
+ // +13dBm, same as power-on default
+ setTxPower(13);
+
+ return true;
+}
+
+// C++ level interrupt handler for this instance
+// RH_RF69 is unusual in Mthat it has several interrupt lines, and not a single, combined one.
+// On Moteino, only one of the several interrupt lines (DI0) from the RH_RF69 is connnected to the processor.
+// We use this to get PACKETSDENT and PAYLOADRADY interrupts.
+void RH_RF69::handleInterrupt()
+{
+ // Get the interrupt cause
+ uint8_t irqflags2 = spiRead(RH_RF69_REG_28_IRQFLAGS2);
+ if (_mode == RHModeTx && (irqflags2 & RH_RF69_IRQFLAGS2_PACKETSENT))
+ {
+ // A transmitter message has been fully sent
+ setModeIdle(); // Clears FIFO
+ _txGood++;
+// Serial.println("PACKETSENT");
+ }
+ // Must look for PAYLOADREADY, not CRCOK, since only PAYLOADREADY occurs _after_ AES decryption
+ // has been done
+ if (_mode == RHModeRx && (irqflags2 & RH_RF69_IRQFLAGS2_PAYLOADREADY))
+ {
+ // A complete message has been received with good CRC
+ _lastRssi = -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1));
+ _lastPreambleTime = millis();
+
+ setModeIdle();
+ // Save it in our buffer
+ readFifo();
+// Serial.println("PAYLOADREADY");
+ }
+}
+
+// Low level function reads the FIFO and checks the address
+// Caution: since we put our headers in what the RH_RF69 considers to be the payload, if encryption is enabled
+// we have to suffer the cost of decryption before we can determine whether the address is acceptable.
+// Performance issue?
+void RH_RF69::readFifo()
+{
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(RH_RF69_REG_00_FIFO); // Send the start address with the write mask off
+ uint8_t payloadlen = _spi.transfer(0); // First byte is payload len (counting the headers)
+ if (payloadlen <= RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN &&
+ payloadlen >= RH_RF69_HEADER_LEN)
+ {
+ _rxHeaderTo = _spi.transfer(0);
+ // Check addressing
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ // Get the rest of the headers
+ _rxHeaderFrom = _spi.transfer(0);
+ _rxHeaderId = _spi.transfer(0);
+ _rxHeaderFlags = _spi.transfer(0);
+ // And now the real payload
+ for (_bufLen = 0; _bufLen < (payloadlen - RH_RF69_HEADER_LEN); _bufLen++)
+ _buf[_bufLen] = _spi.transfer(0);
+ _rxGood++;
+ _rxBufValid = true;
+ }
+ }
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+ // Any junk remaining in the FIFO will be cleared next time we go to receive mode.
+}
+
+// These are low level functions that call the interrupt handler for the correct
+// instance of RH_RF69.
+// 3 interrupts allows us to have 3 different devices
+void RH_RF69::isr0()
+{
+ if (_deviceForInterrupt[0])
+ _deviceForInterrupt[0]->handleInterrupt();
+}
+void RH_RF69::isr1()
+{
+ if (_deviceForInterrupt[1])
+ _deviceForInterrupt[1]->handleInterrupt();
+}
+void RH_RF69::isr2()
+{
+ if (_deviceForInterrupt[2])
+ _deviceForInterrupt[2]->handleInterrupt();
+}
+
+int8_t RH_RF69::temperatureRead()
+{
+ // Caution: must be ins standby.
+// setModeIdle();
+ spiWrite(RH_RF69_REG_4E_TEMP1, RH_RF69_TEMP1_TEMPMEASSTART); // Start the measurement
+ while (spiRead(RH_RF69_REG_4E_TEMP1) & RH_RF69_TEMP1_TEMPMEASRUNNING)
+ ; // Wait for the measurement to complete
+ return 166 - spiRead(RH_RF69_REG_4F_TEMP2); // Very approximate, based on observation
+}
+
+bool RH_RF69::setFrequency(float centre, float afcPullInRange)
+{
+ // Frf = FRF / FSTEP
+ uint32_t frf = (uint32_t)((centre * 1000000.0) / RH_RF69_FSTEP);
+ spiWrite(RH_RF69_REG_07_FRFMSB, (frf >> 16) & 0xff);
+ spiWrite(RH_RF69_REG_08_FRFMID, (frf >> 8) & 0xff);
+ spiWrite(RH_RF69_REG_09_FRFLSB, frf & 0xff);
+
+ // afcPullInRange is not used
+ return true;
+}
+
+int8_t RH_RF69::rssiRead()
+{
+ // Force a new value to be measured
+ // Hmmm, this hangs forever!
+#if 0
+ spiWrite(RH_RF69_REG_23_RSSICONFIG, RH_RF69_RSSICONFIG_RSSISTART);
+ while (!(spiRead(RH_RF69_REG_23_RSSICONFIG) & RH_RF69_RSSICONFIG_RSSIDONE))
+ ;
+#endif
+ return -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1));
+}
+
+void RH_RF69::setOpMode(uint8_t mode)
+{
+ uint8_t opmode = spiRead(RH_RF69_REG_01_OPMODE);
+ opmode &= ~RH_RF69_OPMODE_MODE;
+ opmode |= (mode & RH_RF69_OPMODE_MODE);
+ spiWrite(RH_RF69_REG_01_OPMODE, opmode);
+
+ // Wait for mode to change.
+ while (!(spiRead(RH_RF69_REG_27_IRQFLAGS1) & RH_RF69_IRQFLAGS1_MODEREADY))
+ ;
+}
+
+void RH_RF69::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ if (_power >= 18)
+ {
+ // If high power boost, return power amp to receive mode
+ spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
+ spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
+ }
+ setOpMode(_idleMode);
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_RF69::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ spiWrite(RH_RF69_REG_01_OPMODE, RH_RF69_OPMODE_MODE_SLEEP);
+ _mode = RHModeSleep;
+ }
+ return true;
+}
+
+void RH_RF69::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ if (_power >= 18)
+ {
+ // If high power boost, return power amp to receive mode
+ spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
+ spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
+ }
+ spiWrite(RH_RF69_REG_25_DIOMAPPING1, RH_RF69_DIOMAPPING1_DIO0MAPPING_01); // Set interrupt line 0 PayloadReady
+ setOpMode(RH_RF69_OPMODE_MODE_RX); // Clears FIFO
+ _mode = RHModeRx;
+ }
+}
+
+void RH_RF69::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ if (_power >= 18)
+ {
+ // Set high power boost mode
+ // Note that OCP defaults to ON so no need to change that.
+ spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_BOOST);
+ spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_BOOST);
+ }
+ spiWrite(RH_RF69_REG_25_DIOMAPPING1, RH_RF69_DIOMAPPING1_DIO0MAPPING_00); // Set interrupt line 0 PacketSent
+ setOpMode(RH_RF69_OPMODE_MODE_TX); // Clears FIFO
+ _mode = RHModeTx;
+ }
+}
+
+void RH_RF69::setTxPower(int8_t power)
+{
+ _power = power;
+
+ uint8_t palevel;
+ if (_power < -18)
+ _power = -18;
+
+ // See http://www.hoperf.com/upload/rfchip/RF69-V1.2.pdf section 3.3.6
+ // for power formulas
+ if (_power <= 13)
+ {
+ // -18dBm to +13dBm
+ palevel = RH_RF69_PALEVEL_PA0ON | ((_power + 18) & RH_RF69_PALEVEL_OUTPUTPOWER);
+ }
+ else if (_power >= 18)
+ {
+ // +18dBm to +20dBm
+ // Need PA1+PA2
+ // Also need PA boost settings change when tx is turned on and off, see setModeTx()
+ palevel = RH_RF69_PALEVEL_PA1ON | RH_RF69_PALEVEL_PA2ON | ((_power + 11) & RH_RF69_PALEVEL_OUTPUTPOWER);
+ }
+ else
+ {
+ // +14dBm to +17dBm
+ // Need PA1+PA2
+ palevel = RH_RF69_PALEVEL_PA1ON | RH_RF69_PALEVEL_PA2ON | ((_power + 14) & RH_RF69_PALEVEL_OUTPUTPOWER);
+ }
+ spiWrite(RH_RF69_REG_11_PALEVEL, palevel);
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF69::setModemRegisters(const ModemConfig* config)
+{
+ spiBurstWrite(RH_RF69_REG_02_DATAMODUL, &config->reg_02, 5);
+ spiBurstWrite(RH_RF69_REG_19_RXBW, &config->reg_19, 2);
+ spiWrite(RH_RF69_REG_37_PACKETCONFIG1, config->reg_37);
+}
+
+// Set one of the canned FSK Modem configs
+// Returns true if its a valid choice
+bool RH_RF69::setModemConfig(ModemConfigChoice index)
+{
+ if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
+ return false;
+
+ ModemConfig cfg;
+ memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF69::ModemConfig));
+ setModemRegisters(&cfg);
+
+ return true;
+}
+
+void RH_RF69::setPreambleLength(uint16_t bytes)
+{
+ spiWrite(RH_RF69_REG_2C_PREAMBLEMSB, bytes >> 8);
+ spiWrite(RH_RF69_REG_2D_PREAMBLELSB, bytes & 0xff);
+}
+
+void RH_RF69::setSyncWords(const uint8_t* syncWords, uint8_t len)
+{
+ uint8_t syncconfig = spiRead(RH_RF69_REG_2E_SYNCCONFIG);
+ if (syncWords && len && len <= 4)
+ {
+ spiBurstWrite(RH_RF69_REG_2F_SYNCVALUE1, syncWords, len);
+ syncconfig |= RH_RF69_SYNCCONFIG_SYNCON;
+ }
+ else
+ syncconfig &= ~RH_RF69_SYNCCONFIG_SYNCON;
+ syncconfig &= ~RH_RF69_SYNCCONFIG_SYNCSIZE;
+ syncconfig |= (len-1) << 3;
+ spiWrite(RH_RF69_REG_2E_SYNCCONFIG, syncconfig);
+}
+
+void RH_RF69::setEncryptionKey(uint8_t* key)
+{
+ if (key)
+ {
+ spiBurstWrite(RH_RF69_REG_3E_AESKEY1, key, 16);
+ spiWrite(RH_RF69_REG_3D_PACKETCONFIG2, spiRead(RH_RF69_REG_3D_PACKETCONFIG2) | RH_RF69_PACKETCONFIG2_AESON);
+ }
+ else
+ {
+ spiWrite(RH_RF69_REG_3D_PACKETCONFIG2, spiRead(RH_RF69_REG_3D_PACKETCONFIG2) & ~RH_RF69_PACKETCONFIG2_AESON);
+ }
+}
+
+bool RH_RF69::available()
+{
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx(); // Make sure we are receiving
+ return _rxBufValid;
+}
+
+bool RH_RF69::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+
+ if (buf && len)
+ {
+ ATOMIC_BLOCK_START;
+ if (*len > _bufLen)
+ *len = _bufLen;
+ memcpy(buf, _buf, *len);
+ ATOMIC_BLOCK_END;
+ }
+ _rxBufValid = false; // Got the most recent message
+// printBuffer("recv:", buf, *len);
+ return true;
+}
+
+bool RH_RF69::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_RF69_MAX_MESSAGE_LEN)
+ return false;
+
+ waitPacketSent(); // Make sure we dont interrupt an outgoing message
+ setModeIdle(); // Prevent RX while filling the fifo
+
+ ATOMIC_BLOCK_START;
+ digitalWrite(_slaveSelectPin, LOW);
+ _spi.transfer(RH_RF69_REG_00_FIFO | RH_RF69_SPI_WRITE_MASK); // Send the start address with the write mask on
+ _spi.transfer(len + RH_RF69_HEADER_LEN); // Include length of headers
+ // First the 4 headers
+ _spi.transfer(_txHeaderTo);
+ _spi.transfer(_txHeaderFrom);
+ _spi.transfer(_txHeaderId);
+ _spi.transfer(_txHeaderFlags);
+ // Now the payload
+ while (len--)
+ _spi.transfer(*data++);
+ digitalWrite(_slaveSelectPin, HIGH);
+ ATOMIC_BLOCK_END;
+
+ setModeTx(); // Start the transmitter
+ return true;
+}
+
+uint8_t RH_RF69::maxMessageLength()
+{
+ return RH_RF69_MAX_MESSAGE_LEN;
+}
+
+bool RH_RF69::printRegister(uint8_t reg)
+{
+#ifdef RH_HAVE_SERIAL
+ Serial.print(reg, HEX);
+ Serial.print(" ");
+ Serial.println(spiRead(reg), HEX);
+#endif
+ return true;
+}
+
+bool RH_RF69::printRegisters()
+{
+ uint8_t i;
+ for (i = 0; i < 0x50; i++)
+ printRegister(i);
+ // Non-contiguous registers
+ printRegister(RH_RF69_REG_58_TESTLNA);
+ printRegister(RH_RF69_REG_6F_TESTDAGC);
+ printRegister(RH_RF69_REG_71_TESTAFC);
+
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF69.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,852 @@
+// RH_RF69.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_RF69.h,v 1.29 2015/05/17 00:11:26 mikem Exp $
+//
+///
+
+
+#ifndef RH_RF69_h
+#define RH_RF69_h
+
+#include <RHGenericSPI.h>
+#include <RHSPIDriver.h>
+
+// The crystal oscillator frequency of the RF69 module
+#define RH_RF69_FXOSC 32000000.0
+
+// The Frequency Synthesizer step = RH_RF69_FXOSC / 2^^19
+#define RH_RF69_FSTEP (RH_RF69_FXOSC / 524288)
+
+// This is the maximum number of interrupts the driver can support
+// Most Arduinos can handle 2, Megas can handle more
+#define RH_RF69_NUM_INTERRUPTS 3
+
+// This is the bit in the SPI address that marks it as a write
+#define RH_RF69_SPI_WRITE_MASK 0x80
+
+// Max number of octets the RH_RF69 Rx and Tx FIFOs can hold
+#define RH_RF69_FIFO_SIZE 66
+
+// Maximum encryptable payload length the RF69 can support
+#define RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN 64
+
+// The length of the headers we add.
+// The headers are inside the RF69's payload and are therefore encrypted if encryption is enabled
+#define RH_RF69_HEADER_LEN 4
+
+// This is the maximum message length that can be supported by this driver. Limited by
+// the size of the FIFO, since we are unable to support on-the-fly filling and emptying
+// of the FIFO.
+// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
+// Here we allow for 4 bytes of address and header and payload to be included in the 64 byte encryption limit.
+// the one byte payload length is not encrpyted
+#ifndef RH_RF69_MAX_MESSAGE_LEN
+#define RH_RF69_MAX_MESSAGE_LEN (RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN - RH_RF69_HEADER_LEN)
+#endif
+
+// Keep track of the mode the RF69 is in
+#define RH_RF69_MODE_IDLE 0
+#define RH_RF69_MODE_RX 1
+#define RH_RF69_MODE_TX 2
+
+// This is the default node address,
+#define RH_RF69_DEFAULT_NODE_ADDRESS 0
+
+// Register names
+#define RH_RF69_REG_00_FIFO 0x00
+#define RH_RF69_REG_01_OPMODE 0x01
+#define RH_RF69_REG_02_DATAMODUL 0x02
+#define RH_RF69_REG_03_BITRATEMSB 0x03
+#define RH_RF69_REG_04_BITRATELSB 0x04
+#define RH_RF69_REG_05_FDEVMSB 0x05
+#define RH_RF69_REG_06_FDEVLSB 0x06
+#define RH_RF69_REG_07_FRFMSB 0x07
+#define RH_RF69_REG_08_FRFMID 0x08
+#define RH_RF69_REG_09_FRFLSB 0x09
+#define RH_RF69_REG_0A_OSC1 0x0a
+#define RH_RF69_REG_0B_AFCCTRL 0x0b
+#define RH_RF69_REG_0C_RESERVED 0x0c
+#define RH_RF69_REG_0D_LISTEN1 0x0d
+#define RH_RF69_REG_0E_LISTEN2 0x0e
+#define RH_RF69_REG_0F_LISTEN3 0x0f
+#define RH_RF69_REG_10_VERSION 0x10
+#define RH_RF69_REG_11_PALEVEL 0x11
+#define RH_RF69_REG_12_PARAMP 0x12
+#define RH_RF69_REG_13_OCP 0x13
+#define RH_RF69_REG_14_RESERVED 0x14
+#define RH_RF69_REG_15_RESERVED 0x15
+#define RH_RF69_REG_16_RESERVED 0x16
+#define RH_RF69_REG_17_RESERVED 0x17
+#define RH_RF69_REG_18_LNA 0x18
+#define RH_RF69_REG_19_RXBW 0x19
+#define RH_RF69_REG_1A_AFCBW 0x1a
+#define RH_RF69_REG_1B_OOKPEAK 0x1b
+#define RH_RF69_REG_1C_OOKAVG 0x1c
+#define RH_RF69_REG_1D_OOKFIX 0x1d
+#define RH_RF69_REG_1E_AFCFEI 0x1e
+#define RH_RF69_REG_1F_AFCMSB 0x1f
+#define RH_RF69_REG_20_AFCLSB 0x20
+#define RH_RF69_REG_21_FEIMSB 0x21
+#define RH_RF69_REG_22_FEILSB 0x22
+#define RH_RF69_REG_23_RSSICONFIG 0x23
+#define RH_RF69_REG_24_RSSIVALUE 0x24
+#define RH_RF69_REG_25_DIOMAPPING1 0x25
+#define RH_RF69_REG_26_DIOMAPPING2 0x26
+#define RH_RF69_REG_27_IRQFLAGS1 0x27
+#define RH_RF69_REG_28_IRQFLAGS2 0x28
+#define RH_RF69_REG_29_RSSITHRESH 0x29
+#define RH_RF69_REG_2A_RXTIMEOUT1 0x2a
+#define RH_RF69_REG_2B_RXTIMEOUT2 0x2b
+#define RH_RF69_REG_2C_PREAMBLEMSB 0x2c
+#define RH_RF69_REG_2D_PREAMBLELSB 0x2d
+#define RH_RF69_REG_2E_SYNCCONFIG 0x2e
+#define RH_RF69_REG_2F_SYNCVALUE1 0x2f
+// another 7 sync word bytes follow, 30 through 36 inclusive
+#define RH_RF69_REG_37_PACKETCONFIG1 0x37
+#define RH_RF69_REG_38_PAYLOADLENGTH 0x38
+#define RH_RF69_REG_39_NODEADRS 0x39
+#define RH_RF69_REG_3A_BROADCASTADRS 0x3a
+#define RH_RF69_REG_3B_AUTOMODES 0x3b
+#define RH_RF69_REG_3C_FIFOTHRESH 0x3c
+#define RH_RF69_REG_3D_PACKETCONFIG2 0x3d
+#define RH_RF69_REG_3E_AESKEY1 0x3e
+// Another 15 AES key bytes follow
+#define RH_RF69_REG_4E_TEMP1 0x4e
+#define RH_RF69_REG_4F_TEMP2 0x4f
+#define RH_RF69_REG_58_TESTLNA 0x58
+#define RH_RF69_REG_5A_TESTPA1 0x5a
+#define RH_RF69_REG_5C_TESTPA2 0x5c
+#define RH_RF69_REG_6F_TESTDAGC 0x6f
+#define RH_RF69_REG_71_TESTAFC 0x71
+
+// These register masks etc are named wherever possible
+// corresponding to the bit and field names in the RFM69 Manual
+
+// RH_RF69_REG_01_OPMODE
+#define RH_RF69_OPMODE_SEQUENCEROFF 0x80
+#define RH_RF69_OPMODE_LISTENON 0x40
+#define RH_RF69_OPMODE_LISTENABORT 0x20
+#define RH_RF69_OPMODE_MODE 0x1c
+#define RH_RF69_OPMODE_MODE_SLEEP 0x00
+#define RH_RF69_OPMODE_MODE_STDBY 0x04
+#define RH_RF69_OPMODE_MODE_FS 0x08
+#define RH_RF69_OPMODE_MODE_TX 0x0c
+#define RH_RF69_OPMODE_MODE_RX 0x10
+
+// RH_RF69_REG_02_DATAMODUL
+#define RH_RF69_DATAMODUL_DATAMODE 0x60
+#define RH_RF69_DATAMODUL_DATAMODE_PACKET 0x00
+#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITH_SYNC 0x40
+#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITHOUT_SYNC 0x60
+#define RH_RF69_DATAMODUL_MODULATIONTYPE 0x18
+#define RH_RF69_DATAMODUL_MODULATIONTYPE_FSK 0x00
+#define RH_RF69_DATAMODUL_MODULATIONTYPE_OOK 0x08
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING 0x03
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_NONE 0x00
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT1_0 0x01
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_5 0x02
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_3 0x03
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_NONE 0x00
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_BR 0x01
+#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_2BR 0x02
+
+// RH_RF69_REG_11_PALEVEL
+#define RH_RF69_PALEVEL_PA0ON 0x80
+#define RH_RF69_PALEVEL_PA1ON 0x40
+#define RH_RF69_PALEVEL_PA2ON 0x20
+#define RH_RF69_PALEVEL_OUTPUTPOWER 0x1f
+
+// RH_RF69_REG_23_RSSICONFIG
+#define RH_RF69_RSSICONFIG_RSSIDONE 0x02
+#define RH_RF69_RSSICONFIG_RSSISTART 0x01
+
+// RH_RF69_REG_25_DIOMAPPING1
+#define RH_RF69_DIOMAPPING1_DIO0MAPPING 0xc0
+#define RH_RF69_DIOMAPPING1_DIO0MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING1_DIO0MAPPING_01 0x40
+#define RH_RF69_DIOMAPPING1_DIO0MAPPING_10 0x80
+#define RH_RF69_DIOMAPPING1_DIO0MAPPING_11 0xc0
+
+#define RH_RF69_DIOMAPPING1_DIO1MAPPING 0x30
+#define RH_RF69_DIOMAPPING1_DIO1MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING1_DIO1MAPPING_01 0x10
+#define RH_RF69_DIOMAPPING1_DIO1MAPPING_10 0x20
+#define RH_RF69_DIOMAPPING1_DIO1MAPPING_11 0x30
+
+#define RH_RF69_DIOMAPPING1_DIO2MAPPING 0x0c
+#define RH_RF69_DIOMAPPING1_DIO2MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING1_DIO2MAPPING_01 0x04
+#define RH_RF69_DIOMAPPING1_DIO2MAPPING_10 0x08
+#define RH_RF69_DIOMAPPING1_DIO2MAPPING_11 0x0c
+
+#define RH_RF69_DIOMAPPING1_DIO3MAPPING 0x03
+#define RH_RF69_DIOMAPPING1_DIO3MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING1_DIO3MAPPING_01 0x01
+#define RH_RF69_DIOMAPPING1_DIO3MAPPING_10 0x02
+#define RH_RF69_DIOMAPPING1_DIO3MAPPING_11 0x03
+
+// RH_RF69_REG_26_DIOMAPPING2
+#define RH_RF69_DIOMAPPING2_DIO4MAPPING 0xc0
+#define RH_RF69_DIOMAPPING2_DIO4MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING2_DIO4MAPPING_01 0x40
+#define RH_RF69_DIOMAPPING2_DIO4MAPPING_10 0x80
+#define RH_RF69_DIOMAPPING2_DIO4MAPPING_11 0xc0
+
+#define RH_RF69_DIOMAPPING2_DIO5MAPPING 0x30
+#define RH_RF69_DIOMAPPING2_DIO5MAPPING_00 0x00
+#define RH_RF69_DIOMAPPING2_DIO5MAPPING_01 0x10
+#define RH_RF69_DIOMAPPING2_DIO5MAPPING_10 0x20
+#define RH_RF69_DIOMAPPING2_DIO5MAPPING_11 0x30
+
+#define RH_RF69_DIOMAPPING2_CLKOUT 0x07
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_ 0x00
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_2 0x01
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_4 0x02
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_8 0x03
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_16 0x04
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_32 0x05
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_RC 0x06
+#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_OFF 0x07
+
+// RH_RF69_REG_27_IRQFLAGS1
+#define RH_RF69_IRQFLAGS1_MODEREADY 0x80
+#define RH_RF69_IRQFLAGS1_RXREADY 0x40
+#define RH_RF69_IRQFLAGS1_TXREADY 0x20
+#define RH_RF69_IRQFLAGS1_PLLLOCK 0x10
+#define RH_RF69_IRQFLAGS1_RSSI 0x08
+#define RH_RF69_IRQFLAGS1_TIMEOUT 0x04
+#define RH_RF69_IRQFLAGS1_AUTOMODE 0x02
+#define RH_RF69_IRQFLAGS1_SYNADDRESSMATCH 0x01
+
+// RH_RF69_REG_28_IRQFLAGS2
+#define RH_RF69_IRQFLAGS2_FIFOFULL 0x80
+#define RH_RF69_IRQFLAGS2_FIFONOTEMPTY 0x40
+#define RH_RF69_IRQFLAGS2_FIFOLEVEL 0x20
+#define RH_RF69_IRQFLAGS2_FIFOOVERRUN 0x10
+#define RH_RF69_IRQFLAGS2_PACKETSENT 0x08
+#define RH_RF69_IRQFLAGS2_PAYLOADREADY 0x04
+#define RH_RF69_IRQFLAGS2_CRCOK 0x02
+
+// RH_RF69_REG_2E_SYNCCONFIG
+#define RH_RF69_SYNCCONFIG_SYNCON 0x80
+#define RH_RF69_SYNCCONFIG_FIFOFILLCONDITION_MANUAL 0x40
+#define RH_RF69_SYNCCONFIG_SYNCSIZE 0x38
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_1 0x00
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_2 0x08
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_3 0x10
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_4 0x18
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_5 0x20
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_6 0x28
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_7 0x30
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_8 0x38
+#define RH_RF69_SYNCCONFIG_SYNCSIZE_SYNCTOL 0x07
+
+// RH_RF69_REG_37_PACKETCONFIG1
+#define RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80
+#define RH_RF69_PACKETCONFIG1_DCFREE 0x60
+#define RH_RF69_PACKETCONFIG1_DCFREE_NONE 0x00
+#define RH_RF69_PACKETCONFIG1_DCFREE_MANCHESTER 0x20
+#define RH_RF69_PACKETCONFIG1_DCFREE_WHITENING 0x40
+#define RH_RF69_PACKETCONFIG1_DCFREE_RESERVED 0x60
+#define RH_RF69_PACKETCONFIG1_CRC_ON 0x10
+#define RH_RF69_PACKETCONFIG1_CRCAUTOCLEAROFF 0x08
+#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING 0x06
+#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE 0x00
+#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE 0x02
+#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE_BC 0x04
+#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_RESERVED 0x06
+
+// RH_RF69_REG_3C_FIFOTHRESH
+#define RH_RF69_FIFOTHRESH_TXSTARTCONDITION_NOTEMPTY 0x80
+#define RH_RF69_FIFOTHRESH_FIFOTHRESHOLD 0x7f
+
+// RH_RF69_REG_3D_PACKETCONFIG2
+#define RH_RF69_PACKETCONFIG2_INTERPACKETRXDELAY 0xf0
+#define RH_RF69_PACKETCONFIG2_RESTARTRX 0x04
+#define RH_RF69_PACKETCONFIG2_AUTORXRESTARTON 0x02
+#define RH_RF69_PACKETCONFIG2_AESON 0x01
+
+// RH_RF69_REG_4E_TEMP1
+#define RH_RF69_TEMP1_TEMPMEASSTART 0x08
+#define RH_RF69_TEMP1_TEMPMEASRUNNING 0x04
+
+// RH_RF69_REG_5A_TESTPA1
+#define RH_RF69_TESTPA1_NORMAL 0x55
+#define RH_RF69_TESTPA1_BOOST 0x5d
+
+// RH_RF69_REG_5C_TESTPA2
+#define RH_RF69_TESTPA2_NORMAL 0x70
+#define RH_RF69_TESTPA2_BOOST 0x7c
+
+// RH_RF69_REG_6F_TESTDAGC
+#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_NORMAL 0x00
+#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAON 0x20
+#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAOFF 0x30
+
+// Define this to include Serial printing in diagnostic routines
+#define RH_RF69_HAVE_SERIAL
+
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_RF69 RH_RF69.h <RH_RF69.h>
+/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF69 and compatible radio transceiver.
+///
+/// Works with
+/// - the excellent Moteino and Moteino-USB
+/// boards from LowPowerLab http://lowpowerlab.com/moteino/
+/// - compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H),
+/// - RFM69 modules from http://www.hoperfusa.com such as http://www.hoperfusa.com/details.jsp?pid=145
+/// - Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including
+/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range)
+///
+/// \par Overview
+///
+/// This class provides basic functions for sending and receiving unaddressed,
+/// unreliable datagrams of arbitrary length to 64 octets per packet.
+///
+/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
+/// mesh routers, repeaters, translators etc.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// modulation scheme.
+///
+/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF
+/// RF69B and compatible radio modules, such as the RFM69 module.
+///
+/// The Hope-RF (http://www.hoperf.com) RF69 is a low-cost ISM transceiver
+/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and
+/// programmable data rates. It also suports AES encryption of up to 64 octets
+/// of payload It is available prepackaged on modules such as the RFM69W. And
+/// such modules can be prepacked on processor boards such as the Moteino from
+/// LowPowerLabs (which is what we used to develop the RH_RF69 driver)
+///
+/// This Driver provides functions for sending and receiving messages of up
+/// to 60 octets on any frequency supported by the RF69, in a range of
+/// predefined data rates and frequency deviations. Frequency can be set with
+/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited
+/// range of frequencies due to antenna tuning.
+///
+/// Up to 2 RF69B modules can be connected to an Arduino (3 on a Mega),
+/// permitting the construction of translators and frequency changers, etc.
+///
+/// The following modulation types are suppported with a range of modem configurations for
+/// common data rates and frequency deviations:
+/// - GFSK Gaussian Frequency Shift Keying
+/// - FSK Frequency Shift Keying
+///
+/// Support for other RF69 features such as on-chip temperature measurement,
+/// transmitter power control etc is also provided.
+///
+/// Tested on USB-Moteino with arduino-1.0.5
+/// on OpenSuSE 13.1
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this RH_RF69 Driver conform to this packet format:
+///
+/// - 4 octets PREAMBLE
+/// - 2 octets SYNC 0x2d, 0xd4 (configurable, so you can use this as a network filter)
+/// - 1 octet RH_RF69 payload length
+/// - 4 octets HEADER: (TO, FROM, ID, FLAGS)
+/// - 0 to 60 octets DATA
+/// - 2 octets CRC computed with CRC16(IBM), computed on HEADER and DATA
+///
+/// For technical reasons, the message format is not protocol compatible with the
+/// 'HopeRF Radio Transceiver Message Library for Arduino'
+/// http://www.airspayce.com/mikem/arduino/HopeRF from the same author. Nor is
+/// it compatible with messages sent by 'Virtual Wire'
+/// http://www.airspayce.com/mikem/arduino/VirtualWire.pdf also from the same
+/// author. Nor is it compatible with messages sent by 'RF22'
+/// http://www.airspayce.com/mikem/arduino/RF22 also from the same author.
+///
+/// \par Connecting RFM-69 to Arduino
+///
+/// We tested with Moteino, which is an Arduino Uno compatible with the RFM69W
+/// module on-board. Therefore it needs no connections other than the USB
+/// programming connection and an antenna to make it work.
+///
+/// If you have a bare RFM69W that you want to connect to an Arduino, you
+/// might use these connections (untested): CAUTION: you must use a 3.3V type
+/// Arduino, otherwise you will also need voltage level shifters between the
+/// Arduino and the RFM69. CAUTION, you must also ensure you connect an
+/// antenna
+///
+/// \code
+/// Arduino RFM69W
+/// GND----------GND (ground in)
+/// 3V3----------3.3V (3.3V in)
+/// interrupt 0 pin D2-----------DIO0 (interrupt request out)
+/// SS pin D10----------NSS (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------MOSI (SPI Data in)
+/// MISO pin D12----------MISO (SPI Data out)
+/// \endcode
+///
+/// With these connections, you can then use the default constructor RH_RF69().
+/// You can override the default settings for the SS pin and the interrupt in
+/// the RH_RF69 constructor if you wish to connect the slave select SS to other
+/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53
+/// for Mega) or the interrupt request to other than pin D2 (Caution,
+/// different processors have different constraints as to the pins available
+/// for interrupts).
+///
+/// If you have a Teensy 3.1 and a compatible RFM69 breakout board, you will need to
+/// construct the RH_RF69 instance like this:
+/// \code
+/// RH_RF69 driver(15, 16);
+/// \endcode
+///
+/// If you have a MoteinoMEGA https://lowpowerlab.com/shop/moteinomega
+/// with RFM69 on board, you dont need to make any wiring connections
+/// (the RFM69 module is soldered onto the MotienoMEGA), but you must initialise the RH_RF69
+/// constructor like this:
+/// \code
+/// RH_RF69 driver(4, 2);
+/// \endcode
+/// Make sure you have the MoteinoMEGA core installed in your Arduino hardware folder as described in the
+/// documentation for the MoteinoMEGA.
+///
+/// It is possible to have 2 or more radios connected to one Arduino, provided
+/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common
+/// to all radios)
+///
+/// Caution: on some Arduinos such as the Mega 2560, if you set the slave
+/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may
+/// need to set the usual SS pin to be an output to force the Arduino into SPI
+/// master mode.
+///
+/// Caution: Power supply requirements of the RF69 module may be relevant in some circumstances:
+/// RF69 modules are capable of pulling 45mA+ at full power, where Arduino's 3.3V line can
+/// give 50mA. You may need to make provision for alternate power supply for
+/// the RF69, especially if you wish to use full transmit power, and/or you have
+/// other shields demanding power. Inadequate power for the RF69 is likely to cause symptoms such as:
+/// -reset's/bootups terminate with "init failed" messages
+/// -random termination of communication after 5-30 packets sent/received
+/// -"fake ok" state, where initialization passes fluently, but communication doesn't happen
+/// -shields hang Arduino boards, especially during the flashing
+/// \par Interrupts
+///
+/// The RH_RF69 driver uses interrupts to react to events in the RF69 module,
+/// such as the reception of a new packet, or the completion of transmission
+/// of a packet. The RH_RF69 driver interrupt service routine reads status from
+/// and writes data to the the RF69 module via the SPI interface. It is very
+/// important therefore, that if you are using the RH_RF69 driver with another
+/// SPI based deviced, that you disable interrupts while you transfer data to
+/// and from that other device. Use cli() to disable interrupts and sei() to
+/// reenable them.
+///
+/// \par Memory
+///
+/// The RH_RF69 driver requires non-trivial amounts of memory. The sample
+/// programs above all compile to about 8kbytes each, which will fit in the
+/// flash proram memory of most Arduinos. However, the RAM requirements are
+/// more critical. Therefore, you should be vary sparing with RAM use in
+/// programs that use the RH_RF69 driver.
+///
+/// It is often hard to accurately identify when you are hitting RAM limits on Arduino.
+/// The symptoms can include:
+/// - Mysterious crashes and restarts
+/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements)
+/// - Hanging
+/// - Output from Serial.print() not appearing
+///
+/// \par Automatic Frequency Control (AFC)
+///
+/// The RF69 module is configured by the RH_RF69 driver to always use AFC.
+///
+/// \par Transmitter Power
+///
+/// You can control the transmitter power on the RF69 transceiver
+/// with the RH_RF69::setTxPower() function. The argument can be any of
+/// -18 to +13 (for RF69W) or -14 to 20 (for RF69HW)
+/// The default is 13. Eg:
+/// \code
+/// driver.setTxPower(-5);
+/// \endcode
+///
+/// We have made some actual power measurements against
+/// programmed power for Moteino (with RF69W)
+/// - Moteino (with RF69W), USB power
+/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND
+/// - bnc connecteor
+/// - 12dB attenuator
+/// - BNC-SMA adapter
+/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
+/// - Tektronix TDS220 scope to measure the Vout from power head
+/// \code
+/// Program power Measured Power
+/// dBm dBm
+/// -18 -17
+/// -16 -16
+/// -14 -14
+/// -12 -12
+/// -10 -9
+/// -8 -7
+/// -6 -4
+/// -4 -3
+/// -2 -2
+/// 0 0.2
+/// 2 3
+/// 4 5
+/// 6 7
+/// 8 10
+/// 10 13
+/// 12 14
+/// 13 15
+/// 14 -51
+/// 20 -51
+/// \endcode
+/// We have also made some actual power measurements against
+/// programmed power for Anarduino MiniWireless with RFM69-HW
+/// Anarduino MiniWireless (with RFM69-HW), USB power
+/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND
+/// - bnc connecteor
+/// - 2x12dB attenuators
+/// - BNC-SMA adapter
+/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
+/// - Tektronix TDS220 scope to measure the Vout from power head
+/// \code
+/// Program power Measured Power
+/// dBm dBm
+/// -18 no measurable output
+/// 0 no measurable output
+/// 13 no measurable output
+/// 14 11
+/// 15 12
+/// 16 12.4
+/// 17 14
+/// 18 15
+/// 19 15.8
+/// 20 17
+/// \endcode
+/// (Caution: we dont claim laboratory accuracy for these measurements)
+/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna.
+/// Caution: although the RFM69 appears to have a PC antenna on board, you will get much better power and range even
+/// with just a 1/4 wave wire antenna.
+///
+/// \par Performance
+///
+/// Some simple speed performance tests have been conducted.
+/// In general packet transmission rate will be limited by the modulation scheme.
+/// Also, if your code does any slow operations like Serial printing it will also limit performance.
+/// We disabled any printing in the tests below.
+/// We tested with RH_RF69::GFSK_Rb250Fd250, which is probably the fastest scheme available.
+/// We tested with a 13 octet message length, over a very short distance of 10cm.
+///
+/// Transmission (no reply) tests with modulation RH_RF69::GFSK_Rb250Fd250 and a
+/// 13 octet message show about 152 messages per second transmitted and received.
+///
+/// Transmit-and-wait-for-a-reply tests with modulation RH_RF69::GFSK_Rb250Fd250 and a
+/// 13 octet message (send and receive) show about 68 round trips per second.
+///
+class RH_RF69 : public RHSPIDriver
+{
+public:
+
+ /// \brief Defines register values for a set of modem configuration registers
+ ///
+ /// Defines register values for a set of modem configuration registers
+ /// that can be passed to setModemRegisters() if none of the choices in
+ /// ModemConfigChoice suit your need setModemRegisters() writes the
+ /// register values from this structure to the appropriate RF69 registers
+ /// to set the desired modulation type, data rate and deviation/bandwidth.
+ typedef struct
+ {
+ uint8_t reg_02; ///< Value for register RH_RF69_REG_02_DATAMODUL
+ uint8_t reg_03; ///< Value for register RH_RF69_REG_03_BITRATEMSB
+ uint8_t reg_04; ///< Value for register RH_RF69_REG_04_BITRATELSB
+ uint8_t reg_05; ///< Value for register RH_RF69_REG_05_FDEVMSB
+ uint8_t reg_06; ///< Value for register RH_RF69_REG_06_FDEVLSB
+ uint8_t reg_19; ///< Value for register RH_RF69_REG_19_RXBW
+ uint8_t reg_1a; ///< Value for register RH_RF69_REG_1A_AFCBW
+ uint8_t reg_37; ///< Value for register RH_RF69_REG_37_PACKETCONFIG1
+ } ModemConfig;
+
+ /// Choices for setModemConfig() for a selected subset of common
+ /// modulation types, and data rates. If you need another configuration,
+ /// use the register calculator. and call setModemRegisters() with your
+ /// desired settings.
+ /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic
+ /// definitions and not their integer equivalents: its possible that new values will be
+ /// introduced in later versions (though we will try to avoid it).
+ /// CAUTION: some of these configurations do not work corectly and are marked as such.
+ typedef enum
+ {
+ FSK_Rb2Fd5 = 0, ///< FSK, Whitening, Rb = 2kbs, Fd = 5kHz
+ FSK_Rb2_4Fd4_8, ///< FSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz
+ FSK_Rb4_8Fd9_6, ///< FSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz
+ FSK_Rb9_6Fd19_2, ///< FSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz
+ FSK_Rb19_2Fd38_4, ///< FSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz
+ FSK_Rb38_4Fd76_8, ///< FSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz
+ FSK_Rb57_6Fd120, ///< FSK, Whitening, Rb = 57.6kbs, Fd = 120kHz
+ FSK_Rb125Fd125, ///< FSK, Whitening, Rb = 125kbs, Fd = 125kHz
+ FSK_Rb250Fd250, ///< FSK, Whitening, Rb = 250kbs, Fd = 250kHz
+ FSK_Rb55555Fd50, ///< FSK, Whitening, Rb = 55555kbs,Fd = 50kHz for RFM69 lib compatibility
+
+ GFSK_Rb2Fd5, ///< GFSK, Whitening, Rb = 2kbs, Fd = 5kHz
+ GFSK_Rb2_4Fd4_8, ///< GFSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz
+ GFSK_Rb4_8Fd9_6, ///< GFSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz
+ GFSK_Rb9_6Fd19_2, ///< GFSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz
+ GFSK_Rb19_2Fd38_4, ///< GFSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz
+ GFSK_Rb38_4Fd76_8, ///< GFSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz
+ GFSK_Rb57_6Fd120, ///< GFSK, Whitening, Rb = 57.6kbs, Fd = 120kHz
+ GFSK_Rb125Fd125, ///< GFSK, Whitening, Rb = 125kbs, Fd = 125kHz
+ GFSK_Rb250Fd250, ///< GFSK, Whitening, Rb = 250kbs, Fd = 250kHz
+ GFSK_Rb55555Fd50, ///< GFSK, Whitening, Rb = 55555kbs,Fd = 50kHz
+
+ OOK_Rb1Bw1, ///< OOK, Whitening, Rb = 1kbs, Rx Bandwidth = 1kHz.
+ OOK_Rb1_2Bw75, ///< OOK, Whitening, Rb = 1.2kbs, Rx Bandwidth = 75kHz.
+ OOK_Rb2_4Bw4_8, ///< OOK, Whitening, Rb = 2.4kbs, Rx Bandwidth = 4.8kHz.
+ OOK_Rb4_8Bw9_6, ///< OOK, Whitening, Rb = 4.8kbs, Rx Bandwidth = 9.6kHz.
+ OOK_Rb9_6Bw19_2, ///< OOK, Whitening, Rb = 9.6kbs, Rx Bandwidth = 19.2kHz.
+ OOK_Rb19_2Bw38_4, ///< OOK, Whitening, Rb = 19.2kbs, Rx Bandwidth = 38.4kHz.
+ OOK_Rb32Bw64, ///< OOK, Whitening, Rb = 32kbs, Rx Bandwidth = 64kHz.
+
+// Test,
+ } ModemConfigChoice;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface
+ /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient
+ /// distinct interrupt lines, one for each instance.
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RF69 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
+ /// \param[in] interruptPin The interrupt Pin number that is connected to the RF69 DIO0 interrupt line.
+ /// Defaults to pin 2.
+ /// Caution: You must specify an interrupt capable pin.
+ /// On many Arduino boards, there are limitations as to which pins may be used as interrupts.
+ /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin.
+ /// On other Arduinos pins 2 or 3.
+ /// See http://arduino.cc/en/Reference/attachInterrupt for more details.
+ /// On Chipkit Uno32, pins 38, 2, 7, 8, 35.
+ /// On other boards, any digital pin may be used.
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_RF69(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialises this instance and the radio module connected to it.
+ /// The following steps are taken:
+ /// - Initialise the slave select pin and the SPI interface library
+ /// - Checks the connected RF69 module can be communicated
+ /// - Attaches an interrupt handler
+ /// - Configures the RF69 module
+ /// - Sets the frequency to 434.0 MHz
+ /// - Sets the modem data rate to FSK_Rb2Fd5
+ /// \return true if everything was successful
+ bool init();
+
+ /// Reads the on-chip temperature sensor.
+ /// The RF69 must be in Idle mode (= RF69 Standby) to measure temperature.
+ /// The measurement is uncalibrated and without calibration, you can expect it to be far from
+ /// correct.
+ /// \return The measured temperature, in degrees C from -40 to 85 (uncalibrated)
+ int8_t temperatureRead();
+
+ /// Sets the transmitter and receiver
+ /// centre frequency
+ /// \param[in] centre Frequency in MHz. 240.0 to 960.0. Caution, RF69 comes in several
+ /// different frequency ranges, and setting a frequency outside that range of your radio will probably not work
+ /// \param[in] afcPullInRange Not used
+ /// \return true if the selected frquency centre is within range
+ bool setFrequency(float centre, float afcPullInRange = 0.05);
+
+ /// Reads and returns the current RSSI value.
+ /// Causes the current signal strength to be measured and returned
+ /// If you want to find the RSSI
+ /// of the last received message, use lastRssi() instead.
+ /// \return The current RSSI value on units of 0.5dB.
+ int8_t rssiRead();
+
+ /// Sets the parameters for the RF69 OPMODE.
+ /// This is a low level device access function, and should not normally ned to be used by user code.
+ /// Instead can use stModeRx(), setModeTx(), setModeIdle()
+ /// \param[in] mode RF69 OPMODE to set, one of RH_RF69_OPMODE_MODE_*.
+ void setOpMode(uint8_t mode);
+
+ /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running,
+ /// disables them.
+ void setModeIdle();
+
+ /// If current mode is Tx or Idle, changes it to Rx.
+ /// Starts the receiver in the RF69.
+ void setModeRx();
+
+ /// If current mode is Rx or Idle, changes it to Rx. F
+ /// Starts the transmitter in the RF69.
+ void setModeTx();
+
+ /// Sets the transmitter power output level.
+ /// Be a good neighbour and set the lowest power level you need.
+ /// Caution: legal power limits may apply in certain countries.
+ /// After init(), the power will be set to 13dBm.
+ /// \param[in] power Transmitter power level in dBm. For RF69W, valid values are from -18 to +13
+ /// (higher power settings disable the transmitter).
+ /// For RF69HW, valid values are from +14 to +20. Caution: at +20dBm, duty cycle is limited to 1% and a
+ /// maximum VSWR of 3:1 at the antenna port.
+ void setTxPower(int8_t power);
+
+ /// Sets all the registers required to configure the data modem in the RF69, including the data rate,
+ /// bandwidths etc. You can use this to configure the modem with custom configurations if none of the
+ /// canned configurations in ModemConfigChoice suit you.
+ /// \param[in] config A ModemConfig structure containing values for the modem configuration registers.
+ void setModemRegisters(const ModemConfig* config);
+
+ /// Select one of the predefined modem configurations. If you need a modem configuration not provided
+ /// here, use setModemRegisters() with your own ModemConfig. The default after init() is RH_RF69::GFSK_Rb250Fd250.
+ /// \param[in] index The configuration choice.
+ /// \return true if index is a valid choice.
+ bool setModemConfig(ModemConfigChoice index);
+
+ /// Starts the receiver and checks whether a received message is available.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a complete, valid message has been received and is able to be retrieved by
+ /// recv()
+ bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ bool recv(uint8_t* buf, uint8_t* len);
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is NOT permitted.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ bool send(const uint8_t* data, uint8_t len);
+
+ /// Sets the length of the preamble
+ /// in bytes.
+ /// Caution: this should be set to the same
+ /// value on all nodes in your network. Default is 4.
+ /// Sets the message preamble length in REG_0?_PREAMBLE?SB
+ /// \param[in] bytes Preamble length in bytes.
+ void setPreambleLength(uint16_t bytes);
+
+ /// Sets the sync words for transmit and receive
+ /// Caution: SyncWords should be set to the same
+ /// value on all nodes in your network. Nodes with different SyncWords set will never receive
+ /// each others messages, so different SyncWords can be used to isolate different
+ /// networks from each other. Default is { 0x2d, 0xd4 }.
+ /// \param[in] syncWords Array of sync words, 1 to 4 octets long. NULL if no sync words to be used.
+ /// \param[in] len Number of sync words to set, 1 to 4. 0 if no sync words to be used.
+ void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0);
+
+ /// Enables AES encryption and sets the AES encryption key, used
+ /// to encrypt and decrypt all messages. The default is disabled.
+ /// \param[in] key The key to use. Must be 16 bytes long. The same key must be installed
+ /// in other instances of RF69, otherwise communications will not work correctly. If key is NULL,
+ /// encryption is disabled.
+ void setEncryptionKey(uint8_t* key = NULL);
+
+ /// Returns the time in millis since the most recent preamble was received, and when the most recent
+ /// RSSI measurement was made.
+ uint32_t getLastPreambleTime();
+
+ /// The maximum message length supported by this driver
+ /// \return The maximum message length supported by this driver
+ uint8_t maxMessageLength();
+
+ /// Prints the value of a single register
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging/testing only
+ /// \return true if successful
+ bool printRegister(uint8_t reg);
+
+ /// Prints the value of all the RF69 registers
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging/testing only
+ /// \return true if successful
+ bool printRegisters();
+
+ /// Sets the radio operating mode for the case when the driver is idle (ie not
+ /// transmitting or receiving), allowing you to control the idle mode power requirements
+ /// at the expense of slower transitions to transmit and receive modes.
+ /// By default, the idle mode is RH_RF69_OPMODE_MODE_STDBY,
+ /// but eg setIdleMode(RH_RF69_OPMODE_MODE_SLEEP) will provide a much lower
+ /// idle current but slower transitions. Call this function after init().
+ /// \param[in] idleMode The chip operating mode to use when the driver is idle. One of RH_RF69_OPMODE_*
+ void setIdleMode(uint8_t idleMode);
+
+ /// Sets the radio into low-power sleep mode.
+ /// If successful, the transport will stay in sleep mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
+ /// \return true if sleep mode was successfully entered.
+ virtual bool sleep();
+
+protected:
+ /// This is a low level function to handle the interrupts for one instance of RF69.
+ /// Called automatically by isr*()
+ /// Should not need to be called by user code.
+ void handleInterrupt();
+
+ /// Low level function to read the FIFO and put the received data into the receive buffer
+ /// Should not need to be called by user code.
+ void readFifo();
+
+protected:
+ /// Low level interrupt service routine for RF69 connected to interrupt 0
+ static void isr0();
+
+ /// Low level interrupt service routine for RF69 connected to interrupt 1
+ static void isr1();
+
+ /// Low level interrupt service routine for RF69 connected to interrupt 1
+ static void isr2();
+
+ /// Array of instances connected to interrupts 0 and 1
+ static RH_RF69* _deviceForInterrupt[];
+
+ /// Index of next interrupt number to use in _deviceForInterrupt
+ static uint8_t _interruptCount;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ /// The configured interrupt pin connected to this instance
+ InterruptIn _interruptPin;
+#else
+ /// The configured interrupt pin connected to this instance
+ uint8_t _interruptPin;
+#endif
+
+ /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
+ /// else 0xff
+ uint8_t _myInterruptIndex;
+
+ /// The radio OP mode to use when mode is RHModeIdle
+ uint8_t _idleMode;
+
+ /// The reported device type
+ uint8_t _deviceType;
+
+ /// The selected output power in dBm
+ int8_t _power;
+
+ /// The message length in _buf
+ volatile uint8_t _bufLen;
+
+ /// Array of octets of teh last received message or the next to transmit message
+ uint8_t _buf[RH_RF69_MAX_MESSAGE_LEN];
+
+ /// True when there is a valid message in the Rx buffer
+ volatile bool _rxBufValid;
+
+ /// Time in millis since the last preamble was received (and the last time the RSSI was measured)
+ uint32_t _lastPreambleTime;
+};
+
+/// @example rf69_client.pde
+/// @example rf69_server.pde
+/// @example rf69_reliable_datagram_client.pde
+/// @example rf69_reliable_datagram_server.pde
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF95.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,393 @@
+// RH_RF22.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF95.cpp,v 1.8 2015/08/12 23:18:51 mikem Exp $
+
+#include <RH_RF95.h>
+
+// Interrupt vectors for the 3 Arduino interrupt pins
+// Each interrupt can be handled by a different instance of RH_RF95, allowing you to have
+// 2 or more LORAs per Arduino
+RH_RF95* RH_RF95::_deviceForInterrupt[RH_RF95_NUM_INTERRUPTS] = {0, 0, 0};
+uint8_t RH_RF95::_interruptCount = 0; // Index into _deviceForInterrupt for next device
+
+// These are indexed by the values of ModemConfigChoice
+// Stored in flash (program) memory to save SRAM
+PROGMEM static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] =
+{
+ // 1d, 1e, 26
+ { 0x72, 0x74, 0x00}, // Bw125Cr45Sf128 (the chip default)
+ { 0x92, 0x74, 0x00}, // Bw500Cr45Sf128
+ { 0x48, 0x94, 0x00}, // Bw31_25Cr48Sf512
+ { 0x78, 0xc4, 0x00}, // Bw125Cr48Sf4096
+
+};
+
+RH_RF95::RH_RF95(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi)
+ :
+ RHSPIDriver(slaveSelectPin, spi),
+ _rxBufValid(0),
+ _interruptPin(interruptPin)
+{
+ _myInterruptIndex = 0xff; // Not allocated yet
+}
+
+bool RH_RF95::init()
+{
+ if (!RHSPIDriver::init())
+ return false;
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Determine the interrupt number that corresponds to the interruptPin
+ int interruptNumber = digitalPinToInterrupt(_interruptPin);
+ if (interruptNumber == NOT_AN_INTERRUPT)
+ return false;
+#endif
+
+ // No way to check the device type :-(
+
+ // Set sleep mode, so we can also set LORA mode:
+ spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE);
+ delay(10); // Wait for sleep mode to take over from say, CAD
+ // Check we are in sleep mode, with LORA set
+ if (spiRead(RH_RF95_REG_01_OP_MODE) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE))
+ {
+// Serial.println(spiRead(RH_RF95_REG_01_OP_MODE), HEX);
+ return false; // No device present?
+ }
+
+#if (RH_PLATFORM != RH_PLATFORM_MBED)
+ // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
+ // ARM M4 requires the below. else pin interrupt doesn't work properly.
+ // On all other platforms, its innocuous, belt and braces
+ pinMode(_interruptPin, INPUT);
+#endif
+
+ // Set up interrupt handler
+ // Since there are a limited number of interrupt glue functions isr*() available,
+ // we can only support a limited number of devices simultaneously
+ // ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
+ // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
+ // yourself based on knwledge of what Arduino board you are running on.
+ if (_myInterruptIndex == 0xff)
+ {
+ // First run, no interrupt allocated yet
+ if (_interruptCount <= RH_RF95_NUM_INTERRUPTS)
+ _myInterruptIndex = _interruptCount++;
+ else
+ return false; // Too many devices, not enough interrupt vectors
+ }
+ _deviceForInterrupt[_myInterruptIndex] = this;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ if (_myInterruptIndex == 0)
+ _interruptPin.rise(&isr0);
+ else if (_myInterruptIndex == 1)
+ _interruptPin.rise(&isr1);
+ else if (_myInterruptIndex == 2)
+ _interruptPin.rise(&isr2);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#else
+ if (_myInterruptIndex == 0)
+ attachInterrupt(interruptNumber, isr0, RISING);
+ else if (_myInterruptIndex == 1)
+ attachInterrupt(interruptNumber, isr1, RISING);
+ else if (_myInterruptIndex == 2)
+ attachInterrupt(interruptNumber, isr2, RISING);
+ else
+ return false; // Too many devices, not enough interrupt vectors
+#endif
+ // Set up FIFO
+ // We configure so that we can use the entire 256 byte FIFO for either receive
+ // or transmit, but not both at the same time
+ spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0);
+ spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0);
+
+ // Packet format is preamble + explicit-header + payload + crc
+ // Explicit Header Mode
+ // payload is TO + FROM + ID + FLAGS + message data
+ // RX mode is implmented with RXCONTINUOUS
+ // max message data length is 255 - 4 = 251 octets
+
+ setModeIdle();
+
+ // Set up default configuration
+ // No Sync Words in LORA mode.
+ setModemConfig(Bw125Cr45Sf128); // Radio default
+// setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
+ setPreambleLength(8); // Default is 8
+ // An innocuous ISM frequency, same as RF22's
+ setFrequency(434.0);
+ // Lowish power
+ setTxPower(13);
+
+ return true;
+}
+
+// C++ level interrupt handler for this instance
+// LORA is unusual in that it has several interrupt lines, and not a single, combined one.
+// On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly
+// connnected to the processor.
+// We use this to get RxDone and TxDone interrupts
+void RH_RF95::handleInterrupt()
+{
+ // Read the interrupt register
+ uint8_t irq_flags = spiRead(RH_RF95_REG_12_IRQ_FLAGS);
+ if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR))
+ {
+ _rxBad++;
+ }
+ else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE)
+ {
+ // Have received a packet
+ uint8_t len = spiRead(RH_RF95_REG_13_RX_NB_BYTES);
+
+ // Reset the fifo read ptr to the beginning of the packet
+ spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR));
+ spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len);
+ _bufLen = len;
+ spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
+
+ // Remember the RSSI of this packet
+ // this is according to the doc, but is it really correct?
+ // weakest receiveable signals are reported RSSI at about -66
+ _lastRssi = spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137;
+
+ // We have received a message.
+ validateRxBuf();
+ if (_rxBufValid)
+ setModeIdle(); // Got one
+ }
+ else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE)
+ {
+ _txGood++;
+ setModeIdle();
+ }
+
+ spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
+}
+
+// These are low level functions that call the interrupt handler for the correct
+// instance of RH_RF95.
+// 3 interrupts allows us to have 3 different devices
+void RH_RF95::isr0()
+{
+ if (_deviceForInterrupt[0])
+ _deviceForInterrupt[0]->handleInterrupt();
+}
+void RH_RF95::isr1()
+{
+ if (_deviceForInterrupt[1])
+ _deviceForInterrupt[1]->handleInterrupt();
+}
+void RH_RF95::isr2()
+{
+ if (_deviceForInterrupt[2])
+ _deviceForInterrupt[2]->handleInterrupt();
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_RF95::validateRxBuf()
+{
+ if (_bufLen < 4)
+ return; // Too short to be a real message
+ // Extract the 4 headers
+ _rxHeaderTo = _buf[0];
+ _rxHeaderFrom = _buf[1];
+ _rxHeaderId = _buf[2];
+ _rxHeaderFlags = _buf[3];
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ _rxGood++;
+ _rxBufValid = true;
+ }
+}
+
+bool RH_RF95::available()
+{
+ if (_mode == RHModeTx)
+ return false;
+ setModeRx();
+ return _rxBufValid; // Will be set by the interrupt handler when a good message is received
+}
+
+void RH_RF95::clearRxBuf()
+{
+ ATOMIC_BLOCK_START;
+ _rxBufValid = false;
+ _bufLen = 0;
+ ATOMIC_BLOCK_END;
+}
+
+bool RH_RF95::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+ if (buf && len)
+ {
+ ATOMIC_BLOCK_START;
+ // Skip the 4 headers that are at the beginning of the rxBuf
+ if (*len > _bufLen-RH_RF95_HEADER_LEN)
+ *len = _bufLen-RH_RF95_HEADER_LEN;
+ memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len);
+ ATOMIC_BLOCK_END;
+ }
+ clearRxBuf(); // This message accepted and cleared
+ return true;
+}
+
+bool RH_RF95::send(const uint8_t* data, uint8_t len)
+{
+ if (len > RH_RF95_MAX_MESSAGE_LEN)
+ return false;
+
+ waitPacketSent(); // Make sure we dont interrupt an outgoing message
+ setModeIdle();
+
+ // Position at the beginning of the FIFO
+ spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0);
+ // The headers
+ spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo);
+ spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom);
+ spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId);
+ spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags);
+ // The message data
+ spiBurstWrite(RH_RF95_REG_00_FIFO, data, len);
+ spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN);
+
+ setModeTx(); // Start the transmitter
+ // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
+ return true;
+}
+
+bool RH_RF95::printRegisters()
+{
+#ifdef RH_HAVE_SERIAL
+ uint8_t registers[] = { 0x01, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
+
+ uint8_t i;
+ for (i = 0; i < sizeof(registers); i++)
+ {
+ Serial.print(registers[i], HEX);
+ Serial.print(": ");
+ Serial.println(spiRead(registers[i]), HEX);
+ }
+#endif
+ return true;
+}
+
+uint8_t RH_RF95::maxMessageLength()
+{
+ return RH_RF95_MAX_MESSAGE_LEN;
+}
+
+bool RH_RF95::setFrequency(float centre)
+{
+ // Frf = FRF / FSTEP
+ uint32_t frf = (centre * 1000000.0) / RH_RF95_FSTEP;
+ spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff);
+ spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff);
+ spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff);
+
+ return true;
+}
+
+void RH_RF95::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY);
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_RF95::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP);
+ _mode = RHModeSleep;
+ }
+ return true;
+}
+
+void RH_RF95::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS);
+ spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone
+ _mode = RHModeRx;
+ }
+}
+
+void RH_RF95::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX);
+ spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone
+ _mode = RHModeTx;
+ }
+}
+
+void RH_RF95::setTxPower(int8_t power)
+{
+ if (power > 23)
+ power = 23;
+ if (power < 5)
+ power = 5;
+
+ // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf'
+ // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it
+ // for 21, 22 and 23dBm
+ if (power > 20)
+ {
+ spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE);
+ power -= 3;
+ }
+ else
+ {
+ spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE);
+ }
+
+ // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST
+ // pin is connected, so must use PA_BOOST
+ // Pout = 2 + OutputPower.
+ // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm,
+ // but OutputPower claims it would be 17dBm.
+ // My measurements show 20dBm is correct
+ spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5));
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF95::setModemRegisters(const ModemConfig* config)
+{
+ spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d);
+ spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e);
+ spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26);
+}
+
+// Set one of the canned FSK Modem configs
+// Returns true if its a valid choice
+bool RH_RF95::setModemConfig(ModemConfigChoice index)
+{
+ if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
+ return false;
+
+ ModemConfig cfg;
+ memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig));
+ setModemRegisters(&cfg);
+
+ return true;
+}
+
+void RH_RF95::setPreambleLength(uint16_t bytes)
+{
+ spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8);
+ spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF95.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,603 @@
+// RH_RF95.h
+//
+// Definitions for HopeRF LoRa radios per:
+// http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
+// http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_RF95.h,v 1.7 2015/05/17 00:11:26 mikem Exp $
+//
+
+#ifndef RH_RF95_h
+#define RH_RF95_h
+
+#include <RHSPIDriver.h>
+
+// This is the maximum number of interrupts the driver can support
+// Most Arduinos can handle 2, Megas can handle more
+#define RH_RF95_NUM_INTERRUPTS 3
+
+// Max number of octets the LORA Rx/Tx FIFO can hold
+#define RH_RF95_FIFO_SIZE 255
+
+// This is the maximum number of bytes that can be carried by the LORA.
+// We use some for headers, keeping fewer for RadioHead messages
+#define RH_RF95_MAX_PAYLOAD_LEN RH_RF95_FIFO_SIZE
+
+// The length of the headers we add.
+// The headers are inside the LORA's payload
+#define RH_RF95_HEADER_LEN 4
+
+// This is the maximum message length that can be supported by this driver.
+// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
+// Here we allow for 1 byte message length, 4 bytes headers, user data and 2 bytes of FCS
+#ifndef RH_RF95_MAX_MESSAGE_LEN
+#define RH_RF95_MAX_MESSAGE_LEN (RH_RF95_MAX_PAYLOAD_LEN - RH_RF95_HEADER_LEN)
+#endif
+
+// The crystal oscillator frequency of the module
+#define RH_RF95_FXOSC 32000000.0
+
+// The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19
+#define RH_RF95_FSTEP (RH_RF95_FXOSC / 524288)
+
+
+// Register names (LoRa Mode, from table 85)
+#define RH_RF95_REG_00_FIFO 0x00
+#define RH_RF95_REG_01_OP_MODE 0x01
+#define RH_RF95_REG_02_RESERVED 0x02
+#define RH_RF95_REG_03_RESERVED 0x03
+#define RH_RF95_REG_04_RESERVED 0x04
+#define RH_RF95_REG_05_RESERVED 0x05
+#define RH_RF95_REG_06_FRF_MSB 0x06
+#define RH_RF95_REG_07_FRF_MID 0x07
+#define RH_RF95_REG_08_FRF_LSB 0x08
+#define RH_RF95_REG_09_PA_CONFIG 0x09
+#define RH_RF95_REG_0A_PA_RAMP 0x0a
+#define RH_RF95_REG_0B_OCP 0x0b
+#define RH_RF95_REG_0C_LNA 0x0c
+#define RH_RF95_REG_0D_FIFO_ADDR_PTR 0x0d
+#define RH_RF95_REG_0E_FIFO_TX_BASE_ADDR 0x0e
+#define RH_RF95_REG_0F_FIFO_RX_BASE_ADDR 0x0f
+#define RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR 0x10
+#define RH_RF95_REG_11_IRQ_FLAGS_MASK 0x11
+#define RH_RF95_REG_12_IRQ_FLAGS 0x12
+#define RH_RF95_REG_13_RX_NB_BYTES 0x13
+#define RH_RF95_REG_14_RX_HEADER_CNT_VALUE_MSB 0x14
+#define RH_RF95_REG_15_RX_HEADER_CNT_VALUE_LSB 0x15
+#define RH_RF95_REG_16_RX_PACKET_CNT_VALUE_MSB 0x16
+#define RH_RF95_REG_17_RX_PACKET_CNT_VALUE_LSB 0x17
+#define RH_RF95_REG_18_MODEM_STAT 0x18
+#define RH_RF95_REG_19_PKT_SNR_VALUE 0x19
+#define RH_RF95_REG_1A_PKT_RSSI_VALUE 0x1a
+#define RH_RF95_REG_1B_RSSI_VALUE 0x1b
+#define RH_RF95_REG_1C_HOP_CHANNEL 0x1c
+#define RH_RF95_REG_1D_MODEM_CONFIG1 0x1d
+#define RH_RF95_REG_1E_MODEM_CONFIG2 0x1e
+#define RH_RF95_REG_1F_SYMB_TIMEOUT_LSB 0x1f
+#define RH_RF95_REG_20_PREAMBLE_MSB 0x20
+#define RH_RF95_REG_21_PREAMBLE_LSB 0x21
+#define RH_RF95_REG_22_PAYLOAD_LENGTH 0x22
+#define RH_RF95_REG_23_MAX_PAYLOAD_LENGTH 0x23
+#define RH_RF95_REG_24_HOP_PERIOD 0x24
+#define RH_RF95_REG_25_FIFO_RX_BYTE_ADDR 0x25
+#define RH_RF95_REG_26_MODEM_CONFIG3 0x26
+
+#define RH_RF95_REG_40_DIO_MAPPING1 0x40
+#define RH_RF95_REG_41_DIO_MAPPING2 0x41
+#define RH_RF95_REG_42_VERSION 0x42
+
+#define RH_RF95_REG_4B_TCXO 0x4b
+#define RH_RF95_REG_4D_PA_DAC 0x4d
+#define RH_RF95_REG_5B_FORMER_TEMP 0x5b
+#define RH_RF95_REG_61_AGC_REF 0x61
+#define RH_RF95_REG_62_AGC_THRESH1 0x62
+#define RH_RF95_REG_63_AGC_THRESH2 0x63
+#define RH_RF95_REG_64_AGC_THRESH3 0x64
+
+// RH_RF95_REG_01_OP_MODE 0x01
+#define RH_RF95_LONG_RANGE_MODE 0x80
+#define RH_RF95_ACCESS_SHARED_REG 0x40
+#define RH_RF95_MODE 0x07
+#define RH_RF95_MODE_SLEEP 0x00
+#define RH_RF95_MODE_STDBY 0x01
+#define RH_RF95_MODE_FSTX 0x02
+#define RH_RF95_MODE_TX 0x03
+#define RH_RF95_MODE_FSRX 0x04
+#define RH_RF95_MODE_RXCONTINUOUS 0x05
+#define RH_RF95_MODE_RXSINGLE 0x06
+#define RH_RF95_MODE_CAD 0x07
+
+// RH_RF95_REG_09_PA_CONFIG 0x09
+#define RH_RF95_PA_SELECT 0x80
+#define RH_RF95_OUTPUT_POWER 0x0f
+
+// RH_RF95_REG_0A_PA_RAMP 0x0a
+#define RH_RF95_LOW_PN_TX_PLL_OFF 0x10
+#define RH_RF95_PA_RAMP 0x0f
+#define RH_RF95_PA_RAMP_3_4MS 0x00
+#define RH_RF95_PA_RAMP_2MS 0x01
+#define RH_RF95_PA_RAMP_1MS 0x02
+#define RH_RF95_PA_RAMP_500US 0x03
+#define RH_RF95_PA_RAMP_250US 0x0
+#define RH_RF95_PA_RAMP_125US 0x05
+#define RH_RF95_PA_RAMP_100US 0x06
+#define RH_RF95_PA_RAMP_62US 0x07
+#define RH_RF95_PA_RAMP_50US 0x08
+#define RH_RF95_PA_RAMP_40US 0x09
+#define RH_RF95_PA_RAMP_31US 0x0a
+#define RH_RF95_PA_RAMP_25US 0x0b
+#define RH_RF95_PA_RAMP_20US 0x0c
+#define RH_RF95_PA_RAMP_15US 0x0d
+#define RH_RF95_PA_RAMP_12US 0x0e
+#define RH_RF95_PA_RAMP_10US 0x0f
+
+// RH_RF95_REG_0B_OCP 0x0b
+#define RH_RF95_OCP_ON 0x20
+#define RH_RF95_OCP_TRIM 0x1f
+
+// RH_RF95_REG_0C_LNA 0x0c
+#define RH_RF95_LNA_GAIN 0xe0
+#define RH_RF95_LNA_BOOST 0x03
+#define RH_RF95_LNA_BOOST_DEFAULT 0x00
+#define RH_RF95_LNA_BOOST_150PC 0x11
+
+// RH_RF95_REG_11_IRQ_FLAGS_MASK 0x11
+#define RH_RF95_RX_TIMEOUT_MASK 0x80
+#define RH_RF95_RX_DONE_MASK 0x40
+#define RH_RF95_PAYLOAD_CRC_ERROR_MASK 0x20
+#define RH_RF95_VALID_HEADER_MASK 0x10
+#define RH_RF95_TX_DONE_MASK 0x08
+#define RH_RF95_CAD_DONE_MASK 0x04
+#define RH_RF95_FHSS_CHANGE_CHANNEL_MASK 0x02
+#define RH_RF95_CAD_DETECTED_MASK 0x01
+
+// RH_RF95_REG_12_IRQ_FLAGS 0x12
+#define RH_RF95_RX_TIMEOUT 0x80
+#define RH_RF95_RX_DONE 0x40
+#define RH_RF95_PAYLOAD_CRC_ERROR 0x20
+#define RH_RF95_VALID_HEADER 0x10
+#define RH_RF95_TX_DONE 0x08
+#define RH_RF95_CAD_DONE 0x04
+#define RH_RF95_FHSS_CHANGE_CHANNEL 0x02
+#define RH_RF95_CAD_DETECTED 0x01
+
+// RH_RF95_REG_18_MODEM_STAT 0x18
+#define RH_RF95_RX_CODING_RATE 0xe0
+#define RH_RF95_MODEM_STATUS_CLEAR 0x10
+#define RH_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08
+#define RH_RF95_MODEM_STATUS_RX_ONGOING 0x04
+#define RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED 0x02
+#define RH_RF95_MODEM_STATUS_SIGNAL_DETECTED 0x01
+
+// RH_RF95_REG_1C_HOP_CHANNEL 0x1c
+#define RH_RF95_PLL_TIMEOUT 0x80
+#define RH_RF95_RX_PAYLOAD_CRC_IS_ON 0x40
+#define RH_RF95_FHSS_PRESENT_CHANNEL 0x3f
+
+// RH_RF95_REG_1D_MODEM_CONFIG1 0x1d
+#define RH_RF95_BW 0xc0
+#define RH_RF95_BW_125KHZ 0x00
+#define RH_RF95_BW_250KHZ 0x40
+#define RH_RF95_BW_500KHZ 0x80
+#define RH_RF95_BW_RESERVED 0xc0
+#define RH_RF95_CODING_RATE 0x38
+#define RH_RF95_CODING_RATE_4_5 0x00
+#define RH_RF95_CODING_RATE_4_6 0x08
+#define RH_RF95_CODING_RATE_4_7 0x10
+#define RH_RF95_CODING_RATE_4_8 0x18
+#define RH_RF95_IMPLICIT_HEADER_MODE_ON 0x04
+#define RH_RF95_RX_PAYLOAD_CRC_ON 0x02
+#define RH_RF95_LOW_DATA_RATE_OPTIMIZE 0x01
+
+// RH_RF95_REG_1E_MODEM_CONFIG2 0x1e
+#define RH_RF95_SPREADING_FACTOR 0xf0
+#define RH_RF95_SPREADING_FACTOR_64CPS 0x60
+#define RH_RF95_SPREADING_FACTOR_128CPS 0x70
+#define RH_RF95_SPREADING_FACTOR_256CPS 0x80
+#define RH_RF95_SPREADING_FACTOR_512CPS 0x90
+#define RH_RF95_SPREADING_FACTOR_1024CPS 0xa0
+#define RH_RF95_SPREADING_FACTOR_2048CPS 0xb0
+#define RH_RF95_SPREADING_FACTOR_4096CPS 0xc0
+#define RH_RF95_TX_CONTINUOUS_MOE 0x08
+#define RH_RF95_AGC_AUTO_ON 0x04
+#define RH_RF95_SYM_TIMEOUT_MSB 0x03
+
+// RH_RF95_REG_4D_PA_DAC 0x4d
+#define RH_RF95_PA_DAC_DISABLE 0x04
+#define RH_RF95_PA_DAC_ENABLE 0x07
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_RF95 RH_RF95.h <RH_RF95.h>
+/// \brief Driver to send and receive unaddressed, unreliable datagrams via a LoRa
+/// capable radio transceiver.
+///
+/// For Semtech SX1276/77/78 and HopeRF RFM95/96/97/98 and other similar LoRa capable radios.
+/// Based on http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
+/// and http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
+/// and http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf
+/// and http://www.semtech.com/images/datasheet/sx1276.pdf
+/// FSK/GFSK/OOK modes are not (yet) supported.
+///
+/// Works with
+/// - the excellent MiniWirelessLoRa from Anarduino http://www.anarduino.com/miniwireless
+///
+/// \par Overview
+///
+/// This class provides basic functions for sending and receiving unaddressed,
+/// unreliable datagrams of arbitrary length to 251 octets per packet.
+///
+/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
+/// mesh routers, repeaters, translators etc.
+///
+/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
+/// modulation scheme.
+///
+/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF
+/// RFM95/96/97/98(W) and compatible radio modules in LoRa mode.
+///
+/// The Hope-RF (http://www.hoperf.com) RFM95/96/97/98(W) is a low-cost ISM transceiver
+/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and
+/// programmable data rates, and it also supports the proprietary LoRA (Long Range) mode, which
+/// is the only mode supported in this RadioHead driver.
+///
+/// This Driver provides functions for sending and receiving messages of up
+/// to 251 octets on any frequency supported by the radio, in a range of
+/// predefined Bandwidths, Spreading Factors and Coding Rates. Frequency can be set with
+/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited
+/// range of frequencies due to antenna tuning.
+///
+/// Up to 2 RFM95/96/97/98(W) modules can be connected to an Arduino (3 on a Mega),
+/// permitting the construction of translators and frequency changers, etc.
+///
+/// Support for other features such as transmitter power control etc is
+/// also provided.
+///
+/// Tested on MinWirelessLoRa with arduino-1.0.5
+/// on OpenSuSE 13.1
+///
+/// \par Packet Format
+///
+/// All messages sent and received by this RH_RF95 Driver conform to this packet format:
+///
+/// - LoRa mode:
+/// - 8 symbol PREAMBLE
+/// - Explicit header with header CRC (handled internally by the radio)
+/// - 4 octets HEADER: (TO, FROM, ID, FLAGS)
+/// - 0 to 251 octets DATA
+/// - CRC (handled internally by the radio)
+///
+/// \par Connecting RFM95/96/97/98 to Arduino
+///
+/// We tested with Anarduino MiniWirelessLoRA, which is an Arduino Duemilanove compatible with a RFM96W
+/// module on-board. Therefore it needs no connections other than the USB
+/// programming connection and an antenna to make it work.
+///
+/// If you have a bare RFM95/96/97/98 that you want to connect to an Arduino, you
+/// might use these connections (untested): CAUTION: you must use a 3.3V type
+/// Arduino, otherwise you will also need voltage level shifters between the
+/// Arduino and the RFM95. CAUTION, you must also ensure you connect an
+/// antenna.
+///
+/// \code
+/// Arduino RFM95/96/97/98
+/// GND----------GND (ground in)
+/// 3V3----------3.3V (3.3V in)
+/// interrupt 0 pin D2-----------DIO0 (interrupt request out)
+/// SS pin D10----------NSS (chip select in)
+/// SCK pin D13----------SCK (SPI clock in)
+/// MOSI pin D11----------MOSI (SPI Data in)
+/// MISO pin D12----------MISO (SPI Data out)
+/// \endcode
+///
+/// With these connections, you can then use the default constructor RH_RF95().
+/// You can override the default settings for the SS pin and the interrupt in
+/// the RH_RF95 constructor if you wish to connect the slave select SS to other
+/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53
+/// for Mega) or the interrupt request to other than pin D2 (Caution,
+/// different processors have different constraints as to the pins available
+/// for interrupts).
+///
+/// It is possible to have 2 or more radios connected to one Arduino, provided
+/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common
+/// to all radios)
+///
+/// Caution: on some Arduinos such as the Mega 2560, if you set the slave
+/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may
+/// need to set the usual SS pin to be an output to force the Arduino into SPI
+/// master mode.
+///
+/// Caution: Power supply requirements of the RFM module may be relevant in some circumstances:
+/// RFM95/96/97/98 modules are capable of pulling 120mA+ at full power, where Arduino's 3.3V line can
+/// give 50mA. You may need to make provision for alternate power supply for
+/// the RFM module, especially if you wish to use full transmit power, and/or you have
+/// other shields demanding power. Inadequate power for the RFM is likely to cause symptoms such as:
+/// - reset's/bootups terminate with "init failed" messages
+/// - random termination of communication after 5-30 packets sent/received
+/// - "fake ok" state, where initialization passes fluently, but communication doesn't happen
+/// - shields hang Arduino boards, especially during the flashing
+///
+/// \par Interrupts
+///
+/// The RH_RF95 driver uses interrupts to react to events in the RFM module,
+/// such as the reception of a new packet, or the completion of transmission
+/// of a packet. The RH_RF95 driver interrupt service routine reads status from
+/// and writes data to the the RFM module via the SPI interface. It is very
+/// important therefore, that if you are using the RH_RF95 driver with another
+/// SPI based deviced, that you disable interrupts while you transfer data to
+/// and from that other device. Use cli() to disable interrupts and sei() to
+/// reenable them.
+///
+/// \par Memory
+///
+/// The RH_RF95 driver requires non-trivial amounts of memory. The sample
+/// programs all compile to about 8kbytes each, which will fit in the
+/// flash proram memory of most Arduinos. However, the RAM requirements are
+/// more critical. Therefore, you should be vary sparing with RAM use in
+/// programs that use the RH_RF95 driver.
+///
+/// It is often hard to accurately identify when you are hitting RAM limits on Arduino.
+/// The symptoms can include:
+/// - Mysterious crashes and restarts
+/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements)
+/// - Hanging
+/// - Output from Serial.print() not appearing
+///
+/// \par Range
+///
+/// We have made some simple range tests under the following conditions:
+/// - rf95_client base station connected to a VHF discone antenna at 8m height above ground
+/// - rf95_server mobile connected to 17.3cm 1/4 wavelength antenna at 1m height, no ground plane.
+/// - Both configured for 13dBm, 434MHz, Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range
+/// - Minimum reported RSSI seen for successful comms was about -91
+/// - Range over flat ground through heavy trees and vegetation approx 2km.
+/// - At 20dBm (100mW) otherwise identical conditions approx 3km.
+/// - At 20dBm, along salt water flat sandy beach, 3.2km.
+///
+/// It should be noted that at this data rate, a 12 octet message takes 2 seconds to transmit.
+///
+/// At 20dBm (100mW) with Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on.
+/// (Default medium range) in the conditions described above.
+/// - Range over flat ground through heavy trees and vegetation approx 2km.
+///
+/// \par Transmitter Power
+///
+/// You can control the transmitter power on the RF transceiver
+/// with the RH_RF95::setTxPower() function. The argument can be any of
+/// +5 to +23
+/// The default is 13. Eg:
+/// \code
+/// driver.setTxPower(10);
+/// \endcode
+///
+/// We have made some actual power measurements against
+/// programmed power for Anarduino MiniWirelessLoRa (which has RFM96W-433Mhz installed)
+/// - MiniWirelessLoRa RFM96W-433Mhz, USB power
+/// - 30cm RG316 soldered direct to RFM96W module ANT and GND
+/// - SMA connector
+/// - 12db attenuator
+/// - SMA connector
+/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
+/// - Tektronix TDS220 scope to measure the Vout from power head
+/// \code
+/// Program power Measured Power
+/// dBm dBm
+/// 5 5
+/// 7 7
+/// 9 8
+/// 11 11
+/// 13 13
+/// 15 15
+/// 17 16
+/// 19 18
+/// 20 20
+/// 21 21
+/// 22 22
+/// 23 23
+/// \endcode
+/// (Caution: we dont claim laboratory accuracy for these measurements)
+/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna.
+class RH_RF95 : public RHSPIDriver
+{
+public:
+ /// \brief Defines register values for a set of modem configuration registers
+ ///
+ /// Defines register values for a set of modem configuration registers
+ /// that can be passed to setModemRegisters() if none of the choices in
+ /// ModemConfigChoice suit your need setModemRegisters() writes the
+ /// register values from this structure to the appropriate registers
+ /// to set the desired spreading factor, coding rate and bandwidth
+ typedef struct {
+ uint8_t reg_1d; ///< Value for register RH_RF95_REG_1D_MODEM_CONFIG1
+ uint8_t reg_1e; ///< Value for register RH_RF95_REG_1E_MODEM_CONFIG2
+ uint8_t reg_26; ///< Value for register RH_RF95_REG_26_MODEM_CONFIG3
+ } ModemConfig;
+
+ /// Choices for setModemConfig() for a selected subset of common
+ /// data rates. If you need another configuration,
+ /// determine the necessary settings and call setModemRegisters() with your
+ /// desired settings. It might be helpful to use the LoRa calculator mentioned in
+ /// http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf
+ /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic
+ /// definitions and not their integer equivalents: its possible that new values will be
+ /// introduced in later versions (though we will try to avoid it).
+ typedef enum {
+ Bw125Cr45Sf128 = 0, ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
+ Bw500Cr45Sf128, ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
+ Bw31_25Cr48Sf512, ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
+ Bw125Cr48Sf4096, ///< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range
+ } ModemConfigChoice;
+
+ /// Constructor. You can have multiple instances, but each instance must have its own
+ /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface
+ /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient
+ /// distinct interrupt lines, one for each instance.
+ /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RH_RF22 before
+ /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
+ /// \param[in] interruptPin The interrupt Pin number that is connected to the RFM DIO0 interrupt line.
+ /// Defaults to pin 2, as required by Anarduino MinWirelessLoRa module.
+ /// Caution: You must specify an interrupt capable pin.
+ /// On many Arduino boards, there are limitations as to which pins may be used as interrupts.
+ /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin.
+ /// On other Arduinos pins 2 or 3.
+ /// See http://arduino.cc/en/Reference/attachInterrupt for more details.
+ /// On Chipkit Uno32, pins 38, 2, 7, 8, 35.
+ /// On other boards, any digital pin may be used.
+ /// \param[in] spi Pointer to the SPI interface object to use.
+ /// Defaults to the standard Arduino hardware SPI interface
+ RH_RF95(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi = hardware_spi);
+
+ /// Initialise the Driver transport hardware and software.
+ /// Make sure the Driver is properly configured before calling init().
+ /// \return true if initialisation succeeded.
+ virtual bool init();
+
+ /// Prints the value of all chip registers
+ /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
+ /// For debugging purposes only.
+ /// \return true on success
+ bool printRegisters();
+
+ /// Sets all the registered required to configure the data modem in the RF95/96/97/98, including the bandwidth,
+ /// spreading factor etc. You can use this to configure the modem with custom configurations if none of the
+ /// canned configurations in ModemConfigChoice suit you.
+ /// \param[in] config A ModemConfig structure containing values for the modem configuration registers.
+ void setModemRegisters(const ModemConfig* config);
+
+ /// Select one of the predefined modem configurations. If you need a modem configuration not provided
+ /// here, use setModemRegisters() with your own ModemConfig.
+ /// \param[in] index The configuration choice.
+ /// \return true if index is a valid choice.
+ bool setModemConfig(ModemConfigChoice index);
+
+ /// Tests whether a new message is available
+ /// from the Driver.
+ /// On most drivers, this will also put the Driver into RHModeRx mode until
+ /// a message is actually received by the transport, when it wil be returned to RHModeIdle.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
+ virtual bool available();
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ virtual bool recv(uint8_t* buf, uint8_t* len);
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is permitted.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ virtual bool send(const uint8_t* data, uint8_t len);
+
+ /// Sets the length of the preamble
+ /// in bytes.
+ /// Caution: this should be set to the same
+ /// value on all nodes in your network. Default is 8.
+ /// Sets the message preamble length in RH_RF95_REG_??_PREAMBLE_?SB
+ /// \param[in] bytes Preamble length in bytes.
+ void setPreambleLength(uint16_t bytes);
+
+ /// Returns the maximum message length
+ /// available in this Driver.
+ /// \return The maximum legal message length
+ virtual uint8_t maxMessageLength();
+
+ /// Sets the transmitter and receiver
+ /// centre frequency
+ /// \param[in] centre Frequency in MHz. 137.0 to 1020.0. Caution: RFM95/96/97/98 comes in several
+ /// different frequency ranges, and setting a frequency outside that range of your radio will probably not work
+ /// \return true if the selected frquency centre is within range
+ bool setFrequency(float centre);
+
+ /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running,
+ /// disables them.
+ void setModeIdle();
+
+ /// If current mode is Tx or Idle, changes it to Rx.
+ /// Starts the receiver in the RF95/96/97/98.
+ void setModeRx();
+
+ /// If current mode is Rx or Idle, changes it to Rx. F
+ /// Starts the transmitter in the RF95/96/97/98.
+ void setModeTx();
+
+ /// Sets the transmitter power output level.
+ /// Be a good neighbour and set the lowest power level you need.
+ /// Caution: legal power limits may apply in certain countries. At powers above 20dBm, PA_DAC is enabled.
+ /// After init(), the power will be set to 13dBm.
+ /// \param[in] power Transmitter power level in dBm. For RFM95/96/97/98 LORA, valid values are from +5 to +23
+ void setTxPower(int8_t power);
+
+ /// Sets the radio into low-power sleep mode.
+ /// If successful, the transport will stay in sleep mode until woken by
+ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
+ /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
+ /// \return true if sleep mode was successfully entered.
+ virtual bool sleep();
+
+protected:
+ /// This is a low level function to handle the interrupts for one instance of RH_RF95.
+ /// Called automatically by isr*()
+ /// Should not need to be called by user code.
+ void handleInterrupt();
+
+ /// Examine the revceive buffer to determine whether the message is for this node
+ void validateRxBuf();
+
+ /// Clear our local receive buffer
+ void clearRxBuf();
+
+private:
+ /// Low level interrupt service routine for device connected to interrupt 0
+ static void isr0();
+
+ /// Low level interrupt service routine for device connected to interrupt 1
+ static void isr1();
+
+ /// Low level interrupt service routine for device connected to interrupt 1
+ static void isr2();
+
+ /// Array of instances connected to interrupts 0 and 1
+ static RH_RF95* _deviceForInterrupt[];
+
+ /// Index of next interrupt number to use in _deviceForInterrupt
+ static uint8_t _interruptCount;
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ /// The configured interrupt pin connected to this instance
+ InterruptIn _interruptPin;
+#else
+ /// The configured interrupt pin connected to this instance
+ uint8_t _interruptPin;
+#endif
+
+ /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
+ /// else 0xff
+ uint8_t _myInterruptIndex;
+
+ /// Number of octets in the buffer
+ volatile uint8_t _bufLen;
+
+ /// The receiver/transmitter buffer
+ uint8_t _buf[RH_RF95_MAX_PAYLOAD_LEN];
+
+ /// True when there is a valid message in the buffer
+ volatile bool _rxBufValid;
+};
+
+/// @example rf95_client.pde
+/// @example rf95_server.pde
+/// @example rf95_reliable_datagram_client.pde
+/// @example rf95_reliable_datagram_server.pde
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_TCP.cpp Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,301 @@
+// RH_TCP.cpp
+//
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_TCP.cpp,v 1.5 2015/08/13 02:45:47 mikem Exp $
+
+#include <RadioHead.h>
+
+// This can only build on Linux and compatible systems
+#if (RH_PLATFORM == RH_PLATFORM_UNIX)
+
+#include <RH_TCP.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <string>
+
+RH_TCP::RH_TCP(const char* server)
+ : _server(server),
+ _rxBufLen(0),
+ _rxBufValid(false),
+ _socket(-1)
+{
+}
+
+bool RH_TCP::init()
+{
+ if (!connectToServer())
+ return false;
+ return sendThisAddress(_thisAddress);
+}
+
+bool RH_TCP::connectToServer()
+{
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int sfd, s;
+ struct sockaddr_storage peer_addr;
+ socklen_t peer_addr_len;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // Stream socket
+ hints.ai_flags = AI_PASSIVE; // For wildcard IP address
+ hints.ai_protocol = 0; // Any protocol
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ std::string server(_server);
+ std::string port("4000");
+ size_t indexOfSeparator = server.find_first_of(':');
+ if (indexOfSeparator != std::string::npos)
+ {
+ port = server.substr(indexOfSeparator+1);
+ server.erase(indexOfSeparator);
+ }
+
+ s = getaddrinfo(server.c_str(), port.c_str(), &hints, &result);
+ if (s != 0)
+ {
+ fprintf(stderr, "RH_TCP::connect getaddrinfo failed: %s\n", gai_strerror(s));
+ return false;
+ }
+
+ // getaddrinfo() returns a list of address structures.
+ // Try each address until we successfully connect(2).
+ // If socket(2) (or connect(2)) fails, we (close the socket
+ // and) try the next address. */
+
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+ _socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (_socket == -1)
+ continue;
+
+ if (connect(_socket, rp->ai_addr, rp->ai_addrlen) == 0)
+ break; /* Success */
+
+ close(_socket);
+ }
+
+ if (rp == NULL)
+ { /* No address succeeded */
+ fprintf(stderr, "RH_TCP::connect could not connect to %s\n", _server);
+ return false;
+ }
+
+ freeaddrinfo(result); /* No longer needed */
+
+ // Now make the socket non-blocking
+ int on = 1;
+ int rc = ioctl(_socket, FIONBIO, (char *)&on);
+ if (rc < 0)
+ {
+ fprintf(stderr,"RH_TCP::init failed to set socket non-blocking: %s\n", strerror(errno));
+ close(_socket);
+ _socket = -1;
+ return false;
+ }
+ return true;
+}
+
+void RH_TCP::clearRxBuf()
+{
+ _rxBufValid = false;
+ _rxBufLen = 0;
+}
+
+void RH_TCP::checkForEvents()
+{
+ #define RH_TCP_SOCKETBUF_LEN 500
+ static uint8_t socketBuf[RH_TCP_SOCKETBUF_LEN]; // Room for several messages
+ static uint16_t socketBufLen = 0;
+
+ // Read at most the amount of space we have left in the buffer
+ ssize_t count = read(_socket, socketBuf + socketBufLen, sizeof(socketBuf) - socketBufLen);
+ if (count < 0)
+ {
+ if (errno != EAGAIN)
+ {
+ fprintf(stderr,"RH_TCP::checkForEvents read error: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ else if (count == 0)
+ {
+ // End of file
+ fprintf(stderr,"RH_TCP::checkForEvents unexpected end of file on read\n");
+ exit(1);
+ }
+ else
+ {
+ socketBufLen += count;
+ while (socketBufLen >= 5)
+ {
+ RHTcpTypeMessage* message = ((RHTcpTypeMessage*)socketBuf);
+ uint32_t len = ntohl(message->length);
+ uint32_t messageLen = len + sizeof(message->length);
+ if (len > sizeof(socketBuf) - sizeof(message->length))
+ {
+ // Bogus length
+ fprintf(stderr, "RH_TCP::checkForEvents read ridiculous length: %d. Corrupt message stream? Aborting\n", len);
+ exit(1);
+ }
+ if (socketBufLen >= len + sizeof(message->length))
+ {
+ // Got at least all of this message
+ if (message->type == RH_TCP_MESSAGE_TYPE_PACKET && len >= 5)
+ {
+ // REVISIT: need to check if we are actually receiving?
+ // Its a new packet, extract the headers and payload
+ RHTcpPacket* packet = ((RHTcpPacket*)socketBuf);
+ _rxHeaderTo = packet->to;
+ _rxHeaderFrom = packet->from;
+ _rxHeaderId = packet->id;
+ _rxHeaderFlags = packet->flags;
+ uint32_t payloadLen = len - 5;
+ if (payloadLen <= sizeof(_rxBuf))
+ {
+ // Enough room in our receiver buffer
+ memcpy(_rxBuf, packet->payload, payloadLen);
+ _rxBufLen = payloadLen;
+ _rxBufFull = true;
+ }
+ }
+ // check for other message types here
+ // Now remove the used message by copying the trailing bytes (maybe start of a new message?)
+ // to the top of the buffer
+ memcpy(socketBuf, socketBuf + messageLen, sizeof(socketBuf) - messageLen);
+ socketBufLen -= messageLen;
+ }
+ }
+ }
+}
+
+void RH_TCP::validateRxBuf()
+{
+ // The headers have already been extracted
+ if (_promiscuous ||
+ _rxHeaderTo == _thisAddress ||
+ _rxHeaderTo == RH_BROADCAST_ADDRESS)
+ {
+ _rxGood++;
+ _rxBufValid = true;
+ }
+}
+
+bool RH_TCP::available()
+{
+ if (_socket < 0)
+ return false;
+ checkForEvents();
+ if (_rxBufFull)
+ {
+ validateRxBuf();
+ _rxBufFull= false;
+ }
+ return _rxBufValid;
+}
+
+// Block until something is available
+void RH_TCP::waitAvailable()
+{
+ waitAvailableTimeout(0); // 0 = Wait forever
+}
+
+// Block until something is available or timeout expires
+bool RH_TCP::waitAvailableTimeout(uint16_t timeout)
+{
+ int max_fd;
+ fd_set input;
+ int result;
+
+ FD_ZERO(&input);
+ FD_SET(_socket, &input);
+ max_fd = _socket + 1;
+
+ if (timeout)
+ {
+ struct timeval timer;
+ // Timeout is in milliseconds
+ timer.tv_sec = timeout / 1000;
+ timer.tv_usec = (timeout % 1000) * 1000;
+ result = select(max_fd, &input, NULL, NULL, &timer);
+ }
+ else
+ {
+ result = select(max_fd, &input, NULL, NULL, NULL);
+ }
+ if (result < 0)
+ fprintf(stderr, "RH_TCP::waitAvailableTimeout: select failed %s\n", strerror(errno));
+ return result > 0;
+}
+
+bool RH_TCP::recv(uint8_t* buf, uint8_t* len)
+{
+ if (!available())
+ return false;
+
+ if (buf && len)
+ {
+ if (*len > _rxBufLen)
+ *len = _rxBufLen;
+ memcpy(buf, _rxBuf, *len);
+ }
+ clearRxBuf();
+ return true;
+}
+
+bool RH_TCP::send(const uint8_t* data, uint8_t len)
+{
+ bool ret = sendPacket(data, len);
+ delay(10); // Wait for transmit to succeed. REVISIT: depends on length and speed
+ return ret;
+}
+
+uint8_t RH_TCP::maxMessageLength()
+{
+ return RH_TCP_MAX_MESSAGE_LEN;
+}
+
+void RH_TCP::setThisAddress(uint8_t address)
+{
+ RHGenericDriver::setThisAddress(address);
+ sendThisAddress(_thisAddress);
+}
+
+bool RH_TCP::sendThisAddress(uint8_t thisAddress)
+{
+ if (_socket < 0)
+ return false;
+ RHTcpThisAddress m;
+ m.length = htonl(2);
+ m.type = RH_TCP_MESSAGE_TYPE_THISADDRESS;
+ m.thisAddress = thisAddress;
+ ssize_t sent = write(_socket, &m, sizeof(m));
+ return sent > 0;
+}
+
+bool RH_TCP::sendPacket(const uint8_t* data, uint8_t len)
+{
+ if (_socket < 0)
+ return false;
+ RHTcpPacket m;
+ m.length = htonl(len + 4);
+ m.type = RH_TCP_MESSAGE_TYPE_PACKET;
+ m.to = _txHeaderTo;
+ m.from = _txHeaderFrom;
+ m.id = _txHeaderId;
+ m.flags = _txHeaderFlags;
+ memcpy(m.payload, data, len);
+ ssize_t sent = write(_socket, &m, len + 8);
+ return sent > 0;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_TCP.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,187 @@
+// RH_TCP.h
+// Author: Mike McCauley (mikem@aierspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RH_TCP.h,v 1.4 2015/08/13 02:45:47 mikem Exp $
+#ifndef RH_TCP_h
+#define RH_TCP_h
+
+#include <RHGenericDriver.h>
+#include <RHTcpProtocol.h>
+
+/////////////////////////////////////////////////////////////////////
+/// \class RH_TCP RH_TCP.h <RH_TCP.h>
+/// \brief Driver to send and receive unaddressed, unreliable datagrams via sockets on a Linux simulator
+///
+/// \par Overview
+///
+/// This class is intended to support the testing of RadioHead manager classes and simulated sketches
+/// on a Linux host.
+/// RH_TCP class sends messages to and from other simulator sketches via sockets to a 'Luminiferous Ether'
+/// simulator server (provided).
+/// Multiple instances of simulated clients and servers can run on a single Linux server,
+/// passing messages to each other via the etherSimulator.pl server.
+///
+/// Simple RadioHead sketches can be compiled and run on Linux using a build script and some support files.
+///
+/// \par Running simulated sketches
+///
+/// \code
+/// cd whatever/RadioHead
+/// # build the client for Linux:
+/// tools/simBuild examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde
+/// # build the server for Linux:
+/// tools/simBuild examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde
+/// # in one window, run the simulator server:
+/// tools/etherSimulator.pl
+/// # in another window, run the server
+/// ./simulator_reliable_datagram_server
+/// # in another window, run the client:
+/// ./simulator_reliable_datagram_client
+/// # see output:
+/// Sending to simulator_reliable_datagram_server
+/// got reply from : 0x02: And hello back to you
+/// Sending to simulator_reliable_datagram_server
+/// got reply from : 0x02: And hello back to you
+/// Sending to simulator_reliable_datagram_server
+/// got reply from : 0x02: And hello back to you
+/// ...
+/// \endcode
+///
+/// You can change the listen port and the simulated baud rate with
+/// command line arguments passed to etherSimulator.pl
+///
+/// \par Implementation
+///
+/// etherServer.pl is a conventional server written in Perl.
+/// listens on a TCP socket (defaults to port 4000) for connections from sketch simulators
+/// using RH_TCP as theur driver.
+/// The simulated sketches send messages out to the 'ether' over the TCP connection to the etherServer.
+/// etherServer manages the delivery of each message to any other RH_TCP sketches that are running.
+///
+/// \par Prerequisites
+///
+/// g++ compiler installed and in your $PATH
+/// Perl
+/// Perl POE library
+///
+class RH_TCP : public RHGenericDriver
+{
+public:
+ /// Constructor
+ /// \param[in] server Name and optionally the port number of the ether simulator server to contact.
+ /// Format is "name[:port]", where name can be any valid host name or address (IPV4 or IPV6).
+ /// The trailing :port is optional, and port can be any valid
+ /// port name or port number.
+ RH_TCP(const char* server = "localhost:4000");
+
+ /// Initialise the Driver transport hardware and software.
+ /// Make sure the Driver is properly configured before calling init().
+ /// \return true if initialisation succeeded.
+ virtual bool init();
+
+ /// Tests whether a new message is available
+ /// from the Driver.
+ /// On most drivers, this will also put the Driver into RHModeRx mode until
+ /// a message is actually received by the transport, when it will be returned to RHModeIdle.
+ /// This can be called multiple times in a timeout loop
+ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
+ virtual bool available();
+
+ /// Wait until a new message is available from the driver.
+ /// Blocks until a complete message is received as reported by available()
+ virtual void waitAvailable();
+
+ /// Wait until a new message is available from the driver
+ /// or the timeout expires
+ /// Blocks until a complete message is received as reported by available()
+ /// \param[in] timeout The maximum time to wait in milliseconds
+ /// \return true if a message is available as reported by available()
+ virtual bool waitAvailableTimeout(uint16_t timeout);
+
+ /// Turns the receiver on if it not already on.
+ /// If there is a valid message available, copy it to buf and return true
+ /// else return false.
+ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
+ /// You should be sure to call this function frequently enough to not miss any messages
+ /// It is recommended that you call it in your main loop.
+ /// \param[in] buf Location to copy the received message
+ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
+ /// \return true if a valid message was copied to buf
+ virtual bool recv(uint8_t* buf, uint8_t* len);
+
+ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
+ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
+ /// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will
+ /// return false and will not send the message.
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if the message length was valid and it was correctly queued for transmit
+ virtual bool send(const uint8_t* data, uint8_t len);
+
+ /// Returns the maximum message length
+ /// available in this Driver.
+ /// \return The maximum legal message length
+ virtual uint8_t maxMessageLength();
+
+ /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this.
+ /// This will be used to test the adddress in incoming messages. In non-promiscuous mode,
+ /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted.
+ /// In promiscuous mode, all messages will be accepted regardless of the TO header.
+ /// In a conventional multinode system, all nodes will have a unique address
+ /// (which you could store in EEPROM).
+ /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to,
+ /// allowing the possibilty of address spoofing).
+ /// \param[in] address The address of this node.
+ void setThisAddress(uint8_t address);
+
+protected:
+
+private:
+ /// Connect to the address and port specified by the server constructor argument.
+ /// Prepares the socket for use.
+ bool connectToServer();
+
+ /// Check for new messages from the ether simulator server
+ void checkForEvents();
+
+ /// Clear the receive buffer
+ void clearRxBuf();
+
+ /// Sends thisAddress to the ether simulator server
+ /// in a RHTcpThisAddress message.
+ /// \param[in] thisAddress The node address of this node
+ /// \return true if successful
+ bool sendThisAddress(uint8_t thisAddress);
+
+ /// Sends a message to the ether simulator server for delivery to
+ /// other nodes
+ /// \param[in] data Array of data to be sent
+ /// \param[in] len Number of bytes of data to send (> 0)
+ /// \return true if successful
+ bool sendPacket(const uint8_t* data, uint8_t len);
+
+ /// Address and port of the server to which messages are sent
+ /// and received using the protocol RHTcpPRotocol
+ const char* _server;
+
+ /// The TCP socket used to communicate with the message server
+ int _socket;
+
+ /// Buffer to receive RHTcpProtocol messages
+ uint8_t _rxBuf[RH_TCP_MAX_PAYLOAD_LEN + 5];
+ uint16_t _rxBufLen;
+ bool _rxBufValid;
+
+ /// Check whether the latest received message is complete and uncorrupted
+ void validateRxBuf();
+
+ // Used in the interrupt handlers
+ /// Buf is filled but not validated
+ volatile bool _rxBufFull;
+
+};
+
+/// @example simulator_reliable_datagram_client.pde
+/// @example simulator_reliable_datagram_server.pde
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RadioHead.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,795 @@
+// RadioHead.h
+// Author: Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY
+// Copyright (C) 2014 Mike McCauley
+// $Id: RadioHead.h,v 1.50 2015/08/14 21:20:12 mikem Exp mikem $
+
+/// \mainpage RadioHead Packet Radio library for embedded microprocessors
+///
+/// This is the RadioHead Packet Radio library for embedded microprocessors.
+/// It provides a complete object-oriented library for sending and receiving packetized messages
+/// via a variety of common data radios and other transports on a range of embedded microprocessors.
+///
+/// The version of the package that this documentation refers to can be downloaded
+/// from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.48.zip
+/// You can find the latest version at http://www.airspayce.com/mikem/arduino/RadioHead
+///
+/// You can also find online help and discussion at
+/// http://groups.google.com/group/radiohead-arduino
+/// Please use that group for all questions and discussions on this topic.
+/// Do not contact the author directly, unless it is to discuss commercial licensing.
+/// Before asking a question or reporting a bug, please read
+/// - http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
+/// - http://www.catb.org/esr/faqs/smart-questions.html
+/// - http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
+///
+/// \par Overview
+///
+/// RadioHead consists of 2 main sets of classes: Drivers and Managers.
+///
+/// - Drivers provide low level access to a range of different packet radios and other packetized message transports.
+/// - Managers provide high level message sending and receiving facilities for a range of different requirements.
+///
+/// Every RadioHead program will have an instance of a Driver to provide access to the data radio or transport,
+/// and a Manager that uses that driver to send and receive messages for the application. The programmer is required
+/// to instantiate a Driver and a Manager, and to initialise the Manager. Thereafter the facilities of the Manager
+/// can be used to send and receive messages.
+///
+/// It is also possible to use a Driver on its own, without a Manager, although this only allows unaddressed,
+/// unreliable transport via the Driver's facilities.
+///
+/// In some specialised use cases, it is possible to instantiate more than one Driver and more than one Manager.
+///
+/// A range of different common embedded microprocessor platforms are supported, allowing your project to run
+/// on your choice of processor.
+///
+/// Example programs are included to show the main modes of use.
+///
+/// \par Drivers
+///
+/// The following Drivers are provided:
+///
+/// - RH_RF22
+/// Works with Hope-RF
+/// RF22B and RF23B based transceivers, and compatible chips and modules,
+/// including the RFM22B transceiver module such as
+/// this bare module: http://www.sparkfun.com/products/10153
+/// and this shield: http://www.sparkfun.com/products/11018
+/// and this board: http://www.anarduino.com/miniwireless
+/// and RF23BP modules such as: http://www.anarduino.com/details.jsp?pid=130
+/// Supports GFSK, FSK and OOK. Access to other chip
+/// features such as on-chip temperature measurement, analog-digital
+/// converter, transmitter power control etc is also provided.
+///
+/// - RH_RF24
+/// Works with Silicon Labs Si4460/4461/4463/4464 family of transceivers chip, and the equivalent
+/// HopeRF RF24/26/27 family of chips and the HopeRF RFM24W/26W/27W modules.
+/// Supports GFSK, FSK and OOK. Access to other chip
+/// features such as on-chip temperature measurement, analog-digital
+/// converter, transmitter power control etc is also provided.
+///
+/// - RH_RF69
+/// Works with Hope-RF
+/// RF69B based radio modules, such as the RFM69 module, (as used on the excellent Moteino and Moteino-USB
+/// boards from LowPowerLab http://lowpowerlab.com/moteino/ )
+/// and compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H).
+/// Also works with Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including
+/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range).
+/// Supports GFSK, FSK.
+///
+/// - RH_NRF24
+/// Works with Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 and others.
+/// Also works with Hope-RF RFM73
+/// and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate
+/// with each other.
+///
+/// - RH_NRF905
+/// Works with Nordic nRF905 based 433/868/915 MHz radio modules.
+///
+/// - RH_NRF51
+/// Works with Nordic nRF51 compatible 2.4 GHz SoC/devices such as the nRF51822.
+///
+/// - RH_RF95
+/// Works with Semtech SX1276/77/78 and HopeRF RFM95/96/97/98 and other similar LoRa capable radios.
+/// Supports Long Range (LoRa) with spread spectrum frequency hopping, large payloads etc.
+/// FSK/GFSK/OOK modes are not (yet) supported.
+///
+/// - RH_ASK
+/// Works with a range of inexpensive ASK (amplitude shift keying) RF transceivers such as RX-B1
+/// (also known as ST-RX04-ASK) receiver; TX-C1 transmitter and DR3100 transceiver; FS1000A/XY-MK-5V transceiver;
+/// HopeRF RFM83C / RFM85. Supports ASK (OOK).
+///
+/// - RH_Serial
+/// Works with RS232, RS422, RS485, RS488 and other point-to-point and multidropped serial connections,
+/// or with TTL serial UARTs such as those on Arduino and many other processors,
+/// or with data radios with a
+/// serial port interface. RH_Serial provides packetization and error detection over any hardware or
+/// virtual serial connection. Also builds and runs on Linux and OSX.
+///
+/// - RH_TCP
+/// For use with simulated sketches compiled and running on Linux.
+/// Works with tools/etherSimulator.pl to pass messages between simulated sketches, allowing
+/// testing of Manager classes on Linux and without need for real radios or other transport hardware.
+///
+/// Drivers can be used on their own to provide unaddressed, unreliable datagrams.
+/// All drivers have the same identical API.
+/// Or you can use any Driver with any of the Managers described below.
+///
+/// We welcome contributions of well tested and well documented code to support other transports.
+///
+/// \par Managers
+///
+/// The following Mangers are provided:
+///
+/// - RHDatagram
+/// Addressed, unreliable variable length messages, with optional broadcast facilities.
+///
+/// - RHReliableDatagram
+/// Addressed, reliable, retransmitted, acknowledged variable length messages.
+///
+/// - RHRouter
+/// Multi-hop delivery from source node to destination node via 0 or more intermediate nodes, with manual routing.
+///
+/// - RHMesh
+/// Multi-hop delivery with automatic route discovery and rediscovery.
+///
+/// Any Manager may be used with any Driver.
+///
+/// \par Platforms
+///
+/// A range of platforms is supported:
+///
+/// - Arduino and the Arduino IDE (version 1.0 to 1.6.5 and later)
+/// Including Diecimila, Uno, Mega, Leonardo, Yun, Due, Zero etc. http://arduino.cc/, Also similar boards such as
+/// - Moteino http://lowpowerlab.com/moteino/
+/// - Anarduino Mini http://www.anarduino.com/mini/
+/// - RedBearLab Blend V1.0 http://redbearlab.com/blend/ (with Arduino 1.0.5 and RedBearLab Blend Add-On version 20140701)
+/// - MoteinoMEGA https://lowpowerlab.com/shop/moteinomega
+/// (with Arduino 1.0.5 and the MoteinoMEGA Arduino Core
+/// https://github.com/LowPowerLab/Moteino/tree/master/MEGA/Core)
+/// - etc.
+///
+/// - ChipKit Uno32 board and the MPIDE development environment
+/// http://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-UNO32
+///
+/// - Maple and Flymaple boards with libmaple and the Maple-IDE development environment
+/// http://leaflabs.com/devices/maple/ and http://www.open-drone.org/flymaple
+///
+/// - Teensy including Teensy 3.1 and earlier built using Arduino IDE 1.0.5 to 1.6.4 and later with
+/// teensyduino addon 1.18 to 1.23 and later.
+/// http://www.pjrc.com/teensy
+///
+/// - ATtiny built using Arduino IDE 1.0.5 with the arduino-tiny support from https://code.google.com/p/arduino-tiny/
+/// (Caution: these are very small processors and not all RadioHead features may be available, depending on memory requirements)
+///
+/// - nRF51 compatible Arm chips such as nRF51822 with Arduino 1.6.4 and later using the procedures
+/// in http://redbearlab.com/getting-started-nrf51822/
+///
+/// - Raspberry Pi
+/// Uses BCM2835 library for GPIO http://www.airspayce.com/mikem/bcm2835/
+/// Currently works only with RH_NRF24 driver or other drivers that do not require interrupt support.
+/// Contributed by Mike Poublon.
+///
+/// - Linux and OSX
+/// Using the RHutil/HardwareSerial class, the RH_Serial driver and any manager will
+/// build and run on Linux and OSX. These can be used to build programs that talk securely and reliably to
+/// Arduino and other processors or to other Linux or OSX hosts on a reliable, error detected datagram
+/// protocol over a serial line.
+///
+/// Other platforms are partially supported, such as Generic AVR 8 bit processors, MSP430.
+/// We welcome contributions that will expand the range of supported platforms.
+///
+/// RadioHead is available (through the efforts of others)
+/// for PlatformIO. PlatformIO is a cross-platform code builder and the missing library manager.
+/// http://platformio.org/#!/lib/show/124/RadioHead
+///
+/// \par History
+///
+/// RadioHead was created in April 2014, substantially based on code from some of our other earlier Radio libraries:
+///
+/// - RHMesh, RHRouter, RHReliableDatagram and RHDatagram are derived from the RF22 library version 1.39.
+/// - RH_RF22 is derived from the RF22 library version 1.39.
+/// - RH_RF69 is derived from the RF69 library version 1.2.
+/// - RH_ASK is based on the VirtualWire library version 1.26, after significant conversion to C++.
+/// - RH_Serial was new.
+/// - RH_NRF24 is based on the NRF24 library version 1.12, with some significant changes.
+///
+/// During this combination and redevelopment, we have tried to retain all the processor dependencies and support from
+/// the libraries that were contributed by other people. However not all platforms can be tested by us, so if you
+/// find that support from some platform has not been successfully migrated, please feel free to fix it and send us a
+/// patch.
+///
+/// Users of RHMesh, RHRouter, RHReliableDatagram and RHDatagram in the previous RF22 library will find that their
+/// existing code will run mostly without modification. See the RH_RF22 documentation for more details.
+///
+/// \par Installation
+///
+/// Install in the usual way: unzip the distribution zip file to the libraries
+/// sub-folder of your sketchbook.
+/// The example sketches will be visible in in your Arduino, mpide, maple-ide or whatever.
+/// http://arduino.cc/en/Guide/Libraries
+///
+/// \par Compatible Hardware Suppliers
+///
+/// We have had good experiences with the following suppliers of RadioHead compatible hardware:
+///
+/// - LittleBird http://littlebirdelectronics.com.au in Australia for all manner of Arduinos and radios.
+/// - LowPowerLab http://lowpowerlab.com/moteino in USA for the excellent Moteino and Moteino-USB
+/// boards which include Hope-RF RF69B radios on-board.
+/// - Anarduino and HopeRF USA (http://www.hoperfusa.com and http://www.anarduino.com) who have a wide range
+/// of HopeRF radios and Arduino integrated modules.
+/// - SparkFun https://www.sparkfun.com/ in USA who design and sell a wide range of Arduinos and radio modules.
+///
+/// \par Donations
+///
+/// This library is offered under a free GPL license for those who want to use it that way.
+/// We try hard to keep it up to date, fix bugs
+/// and to provide free support. If this library has helped you save time or money, please consider donating at
+/// http://www.airspayce.com or here:
+///
+/// \htmlonly <form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="mikem@airspayce.com" /> <input type="hidden" name="lc" value="AU" /> <input type="hidden" name="item_name" value="Airspayce" /> <input type="hidden" name="item_number" value="RadioHead" /> <input type="hidden" name="currency_code" value="USD" /> <input type="hidden" name="bn" value="PP-DonationsBF:btn_donateCC_LG.gif:NonHosted" /> <input type="image" alt="PayPal — The safer, easier way to pay online." name="submit" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" /> <img alt="" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" border="0" /></form> \endhtmlonly
+///
+/// \par Trademarks
+///
+/// RadioHead is a trademark of AirSpayce Pty Ltd. The RadioHead mark was first used on April 12 2014 for
+/// international trade, and is used only in relation to data communications hardware and software and related services.
+/// It is not to be confused with any other similar marks covering other goods and services.
+///
+/// \par Copyright
+///
+/// This software is Copyright (C) 2011-2014 Mike McCauley. Use is subject to license
+/// conditions. The main licensing options available are GPL V2 or Commercial:
+///
+/// \par Open Source Licensing GPL V2
+///
+/// This is the appropriate option if you want to share the source code of your
+/// application with everyone you distribute it to, and you also want to give them
+/// the right to share who uses it. If you wish to use this software under Open
+/// Source Licensing, you must contribute all your source code to the open source
+/// community in accordance with the GPL Version 2 when your application is
+/// distributed. See http://www.gnu.org/copyleft/gpl.html
+///
+/// \par Commercial Licensing
+///
+/// This is the appropriate option if you are creating proprietary applications
+/// and you are not prepared to distribute and share the source code of your
+/// application. Contact info@airspayce.com for details (do not use this address for anything other than
+/// commercial license enquiries. For all other queries, using the RadioHead mailing list).
+///
+/// \par Revision History
+/// \version 1.1 2014-04-14<br>
+/// Initial public release
+/// \version 1.2 2014-04-23<br>
+/// Fixed various typos. <br>
+/// Added links to compatible Anarduino products.<br>
+/// Added RHNRFSPIDriver, RH_NRF24 classes to support Nordic NRF24 based radios.
+/// \version 1.3 2014-04-28<br>
+/// Various documentation fixups.<br>
+/// RHDatagram::setThisAddress() did not set the local copy of thisAddress. Reported by Steve Childress.<br>
+/// Fixed a problem on Teensy with RF22 and RF69, where the interrupt pin needs to be set for input, <br>
+/// else pin interrupt doesn't work properly. Reported by Steve Childress and patched by
+/// Adrien van den Bossche. Thanks.<br>
+/// Fixed a problem that prevented RF22 honouring setPromiscuous(true). Reported by Steve Childress.<br>
+/// Updated documentation to clarify some issues to do with maximum message lengths
+/// reported by Steve Childress.<br>
+/// Added support for yield() on systems that support it (currently Arduino 1.5.5 and later)
+/// so that spin-loops can suport multitasking. Suggested by Steve Childress.<br>
+/// Added RH_RF22::setGpioReversed() so the reversal it can be configured at run-time after
+/// radio initialisation. It must now be called _after_ init(). Suggested by Steve Childress.<br>
+/// \version 1.4 2014-04-29<br>
+/// Fixed further problems with Teensy compatibility for RH_RF22. Tested on Teensy 3.1.
+/// The example/rf22_* examples now run out of the box with the wiring connections as documented for Teensy
+/// in RH_RF22.<br>
+/// Added YIELDs to spin-loops in RHRouter, RHMesh and RHReliableDatagram, RH_NRF24.<br>
+/// Tested RH_Serial examples with Teensy 3.1: they now run out of the box.<br>
+/// Tested RH_ASK examples with Teensy 3.1: they now run out of the box.<br>
+/// Reduced default SPI speed for NRF24 from 8MHz to 1MHz on Teensy, to improve reliability when
+/// poor wiring is in use.<br>
+/// on some devices such as Teensy.<br>
+/// Tested RH_NRF24 examples with Teensy 3.1: they now run out of the box.<br>
+/// \version 1.5 2014-04-29<br>
+/// Added support for Nordic Semiconductor nRF905 transceiver with RH_NRF905 driver. Also
+/// added examples for nRF905 and tested on Teensy 3.1
+/// \version 1.6 2014-04-30<br>
+/// NRF905 examples were missing
+/// \version 1.7 2014-05-03<br>
+/// Added support for Arduino Due. Tested with RH_NRF905, RH_Serial, RH_ASK.
+/// IMPORTANT CHANGE to interrupt pins on Arduino with RH_RF22 and RH_RF69 constructors:
+/// previously, you had to specify the interrupt _number_ not the interrupt _pin_. Arduinos and Uno32
+/// are now consistent with all other platforms: you must specify the interrupt pin number. Default
+/// changed to pin 2 (a common choice with RF22 shields).
+/// Removed examples/maple/maple_rf22_reliable_datagram_client and
+/// examples/maple/maple_rf22_reliable_datagram_client since the rf22 examples now work out
+/// of the box with Flymaple.
+/// Removed examples/uno32/uno32_rf22_reliable_datagram_client and
+/// examples/uno32/uno32_rf22_reliable_datagram_client since the rf22 examples now work out
+/// of the box with ChipKit Uno32.
+/// \version 1.8 2014-05-08 <br>
+/// Added support for YIELD in Teensy 2 and 3, suggested by Steve Childress.<br>
+/// Documentation updates. Clarify use of headers and Flags<br>
+/// Fixed misalignment in RH_RF69 between ModemConfigChoice definitions and the implemented choices
+/// which meant you didnt get the choice you thought and GFSK_Rb55555Fd50 hung the transmitter.<br>
+/// Preliminary work on Linux simulator.
+/// \version 1.9 2014-05-14 <br>
+/// Added support for using Timer 2 instead of Timer 1 on Arduino in RH_ASK when
+/// RH_ASK_ARDUINO_USE_TIMER2 is defined. With the kind assistance of
+/// Luc Small. Thanks!<br>
+/// Updated comments in RHReliableDatagram concerning servers, retries, timeouts and delays.
+/// Fixed an error in RHReliableDatagram where recvfrom return value was not checked.
+/// Reported by Steve Childress.<br>
+/// Added Linux simulator support so simple RadioHead sketches can be compiled and run on Linux.<br>
+/// Added RH_TCP driver to permit message passing between simulated sketches on Linux.<br>
+/// Added example simulator sketches.<br>
+/// Added tools/etherSimulator.pl, a simulator of the 'Luminiferous Ether' that passes
+/// messages between simulated sketches and can simulate random message loss etc.<br>
+/// Fixed a number of typos and improved some documentation.<br>
+/// \version 1.10 2014-05-15 <br>
+/// Added support for RFM73 modules to RH_NRF24. These 2 radios are very similar, and can interoperate
+/// with each other. Added new RH_NRF24::TransmitPower enums for the RFM73, which has a different
+/// range of available powers<br>
+/// reduced the default SPI bus speed for RH_NRF24 to 1MHz, since so many modules and CPU have problems
+/// with 8MHz.<br>
+/// \version 1.11 2014-05-18<br>
+/// Testing RH_RF22 with RFM23BP and 3.3V Teensy 3.1 and 5V Arduinos.
+/// Updated documentation with respect to GPIO and antenna
+/// control pins for RFM23. Updated documentation with respect to transmitter power control for RFM23<br>
+/// Fixed a problem with RH_RF22 driver, where GPIO TX and RX pins were not configured during
+/// initialisation, causing poor transmit power and sensitivity on those RF22/RF23 devices where GPIO controls
+/// the antenna selection pins.
+/// \version 1.12 2014-05-20<br>
+/// Testing with RF69HW and the RH_RF69 driver. Works well with the Anarduino MiniWireless -CW and -HW
+/// boards http://www.anarduino.com/miniwireless/ including
+/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range).<br>
+/// Clarified documentation of RH_RF69::setTxPower values for different models of RF69.<br>
+/// Added RHReliableDatagram::resetRetransmissions().<br>
+/// Retransmission count precision increased to uin32_t.<br>
+/// Added data about actual power measurements from RFM22 module.<br>
+/// \version 1.13 2014-05-23<br>
+/// setHeaderFlags(flags) changed to setHeaderFlags(set, clear), enabling any flags to be
+/// individually set and cleared by either RadioHead or application code. Requested by Steve Childress.<br>
+/// Fixed power output setting for boost power on RF69HW for 18, 19 and 20dBm.<br>
+/// Added data about actual power measurements from RFM69W and RFM69HW modules.<br>
+/// \version 1.14 2014-05-26<br>
+/// RH_RF69::init() now always sets the PA boost back to the default settings, else can get invalid
+/// PA power modes after uploading new sketches without a power cycle. Reported by Bryan.<br>
+/// Added new macros RH_VERSION_MAJOR RH_VERSION_MINOR, with automatic maintenance in Makefile.<br>
+/// Improvements to RH_TCP: constructor now honours the server argument in the form "servername:port".<br>
+/// Added YIELD to RHReliableDatagram::recvfromAckTimeout. Requested by Steve Childress.<br>
+/// Fixed a problem with RH_RF22 reliable datagram acknowledgements that was introduced in version 1.13.
+/// Reported by Steve Childress.<br>
+/// \version 1.15 2014-05-27<br>
+/// Fixed a problem with the RadioHead .zip link.
+/// \version 1.16 2014-05-30 <br>
+/// Fixed RH_RF22 so that lastRssi() returns the signal strength in dBm. Suggested by Steve Childress.<br>
+/// Added support for getLastPreambleTime() to RH_RF69. Requested by Steve Childress.<br>
+/// RH_NRF24::init() now checks if there is a device connected and responding, else init() will fail.
+/// Suggested by Steve Brown.<br>
+/// RHSoftwareSPI now initialises default values for SPI pins MOSI = 12, MISO = 11 and SCK = 13.<br>
+/// Fixed some problems that prevented RH_NRF24 working with mixed software and hardware SPI
+/// on different devices: a race condition
+/// due to slow SPI transfers and fast acknowledgement.<br>
+/// \version 1.17 2014-06-02 <br>
+/// Fixed a debug typo in RHReliableDatagram that was introduced in 1.16.<br>
+/// RH_NRF24 now sets default power, data rate and channel in init(), in case another
+/// app has previously set different values without powerdown.<br>
+/// Caution: there are still problems with RH_NRF24 and Software SPI. Do not use.<br>
+/// \version 1.18 2014-06-02<br>
+/// Improvements to performance of RH_NRF24 statusRead, allowing RH_NRF24 and Software SPI
+/// to operate on slow devices like Arduino Uno.<br>
+/// \version 1.19 2014-06-19<br>
+/// Added examples ask_transmitter.pde and ask_receiver.pde.<br>
+/// Fixed an error in the RH_RF22 doc for connection of Teensy to RF22.<br>
+/// Improved documentation of start symbol bit patterns in RH_ASK.cpp
+/// \version 1.20 2014-06-24<br>
+/// Fixed a problem with compiling on platforms such as ATTiny where SS is not defined.<br>
+/// Added YIELD to RHMesh::recvfromAckTimeout().<br>
+/// \version 1.21 2014-06-24<br>
+/// Fixed an issue in RH_Serial where characters might be lost with back-to-back frames.
+/// Suggested by Steve Childress.<br>
+/// Brought previous RHutil/crc16.h code into mainline RHCRC.cpp to prevent name collisions
+/// with other similarly named code in other libraries. Suggested by Steve Childress.<br>
+/// Fix SPI bus speed errors on 8MHz Arduinos.
+/// \version 1.22 2014-07-01<br>
+/// Update RH_ASK documentation for common wiring connections.<br>
+/// Testing RH_ASK with HopeRF RFM83C/RFM85 courtesy Anarduino http://www.anarduino.com/<br>
+/// Testing RH_NRF24 with Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html
+/// using both hardware SPI on the ITDB02 Parallel LCD Module Interface pins and software SPI
+/// on the nRF24L01+ Module Interface pins. Documented wiring required.<br>
+/// Added support for AVR 1284 and 1284p, contributed by Peter Scargill.
+/// Added support for Semtech SX1276/77/78 and HopeRF RFM95/96/97/98 and other similar LoRa capable radios
+/// in LoRa mode only. Tested with the excellent MiniWirelessLoRa from
+/// Anarduino http://www.anarduino.com/miniwireless<br>
+/// \version 1.23 2014-07-03<br>
+/// Changed the default modulation for RH_RF69 to GFSK_Rb250Fd250, since the previous default
+/// was not very reliable.<br>
+/// Documented RH_RF95 range tests.<br>
+/// Improvements to RH_RF22 RSSI readings so that lastRssi correctly returns the last message in dBm.<br>
+/// \version 1.24 2014-07-18
+/// Added support for building RadioHead for STM32F4 Discovery boards, using the native STM Firmware libraries,
+/// in order to support Codec2WalkieTalkie (http://www.airspayce.com/mikem/Codec2WalkieTalkie)
+/// and other projects. See STM32ArduinoCompat.<br>
+/// Default modulation for RH_RF95 was incorrectly set to a very slow Bw125Cr48Sf4096
+/// \version 1.25 2014-07-25
+/// The available() function will longer terminate any current transmission, and force receive mode.
+/// Now, if there is no unprocessed incoming message and an outgoing message is currently being transmitted,
+/// available() will return false.<br>
+/// RHRouter::sendtoWait(uint8_t*, uint8_t, uint8_t, uint8_t) renamed to sendtoFromSourceWait due to conflicts
+/// with new sendtoWait() with optional flags.<br>
+/// RHMEsh and RHRouter already supported end-to-end application layer flags, but RHMesh::sendtoWait()
+/// and RHRouter::sendToWait have now been extended to expose a way to send optional application layer flags.
+/// \version 1.26 2014-08-12
+/// Fixed a Teensy 2.0 compile problem due yield() not available on Teensy < 3.0. <br>
+/// Adjusted the algorithm of RH_RF69::temperatureRead() to more closely reflect reality.<br>
+/// Added functions to RHGenericDriver to get driver packet statistics: rxBad(), rxGood(), txGood().<br>
+/// Added RH_RF69::printRegisters().<br>
+/// RH_RF95::printRegisters() was incorrectly printing the register index instead of the address.
+/// Reported by Phang Moh Lim.<br>
+/// RH_RF95, added definitions for some more registers that are usable in LoRa mode.<br>
+/// RH_RF95::setTxPower now uses RH_RF95_PA_DAC_ENABLE to achieve 21, 22 and 23dBm.<br>
+/// RH_RF95, updated power output measurements.<br>
+/// Testing RH_RF69 on Teensy 3.1 with RF69 on PJRC breakout board. OK.<br>
+/// Improvements so RadioHead will build under Arduino where SPI is not supported, such as
+/// ATTiny.<br>
+/// Improvements so RadioHead will build for ATTiny using Arduino IDE and tinycore arduino-tiny-0100-0018.zip.<br>
+/// Testing RH_ASK on ATTiny85. Reduced RAM footprint.
+/// Added helpful documentation. Caution: RAM memory is *very* tight on this platform.<br>
+/// RH_RF22 and RH_RF69, added setIdleMode() function to allow the idle mode radio operating state
+/// to be controlled for lower idle power consumption at the expense of slower transitions to TX and RX.<br>
+/// \version 1.27 2014-08-13
+/// All RH_RF69 modulation schemes now have data whitening enabled by default.<br>
+/// Tested and added a number of OOK modulation schemes to RH_RF69 Modem config table.<br>
+/// Minor improvements to a number of the faster RH_RF69 modulation schemes, but some slower ones
+/// are still not working correctly.<br>
+/// \version 1.28 2014-08-20
+/// Added new RH_RF24 driver to support Si446x, RF24/26/26, RFM24/26/27 family of transceivers.
+/// Tested with the excellent
+/// Anarduino Mini and RFM24W and RFM26W with the generous assistance of the good people at
+/// Anarduino http://www.anarduino.com.
+/// \version 1.29 2014-08-21
+/// Fixed a compile error in RH_RF24 introduced at the last minute in hte previous release.<br>
+/// Improvements to RH_RF69 modulation schemes: now include the AFCBW in teh ModemConfig.<br>
+/// ModemConfig RH_RF69::FSK_Rb2Fd5 and RH_RF69::GFSK_Rb2Fd5 are now working.<br>
+/// \version 1.30 2014-08-25
+/// Fixed some compile problems with ATtiny84 on Arduino 1.5.5 reported by Glen Cook.<br>
+/// \version 1.31 2014-08-27
+/// Changed RH_RF69 FSK and GFSK modulations from Rb2_4Fd2_4 to Rb2_4Fd4_8 and FSK_Rb4_8Fd4_8 to FSK_Rb4_8Fd9_6
+/// since the previous ones were unreliable (they had modulation indexes of 1).<br>
+/// \version 1.32 2014-08-28
+/// Testing with RedBearLab Blend board http://redbearlab.com/blend/. OK.<br>
+/// Changed more RH_RF69 FSK and GFSK slowish modulations to have modulation index of 2 instead of 1.
+/// This required chnaging the symbolic names.<br>
+/// \version 1.33 2014-09-01
+/// Added support for sleep mode in RHGeneric driver, with new mode
+/// RHModeSleep and new virtual function sleep().<br>
+/// Added support for sleep to RH_RF69, RH_RF22, RH_NRF24, RH_RF24, RH_RF95 drivers.<br>
+/// \version 1.34 2014-09-19
+/// Fixed compile errors in example rf22_router_test.<br>
+/// Fixed a problem with RH_NRF24::setNetworkAddress, also improvements to RH_NRF24 register printing.
+/// Patched by Yveaux.<br>
+/// Improvements to RH_NRF24 initialisation for version 2.0 silicon.<br>
+/// Fixed problem with ambigiguous print call in RH_RFM69 when compiling for Codec2.<br>
+/// Fixed a problem with RH_NRF24 on RFM73 where the LNA gain was not set properly, reducing the sensitivity
+/// of the receiver.
+/// \version 1.35 2014-09-19
+/// Fixed a problem with interrupt setup on RH_RF95 with Teensy3.1. Reported by AD.<br>
+/// \version 1.36 2014-09-22
+/// Improvements to interrupt pin assignments for __AVR_ATmega1284__ and__AVR_ATmega1284P__, provided by
+/// Peter Scargill.<br>
+/// Work around a bug in Arduino 1.0.6 where digitalPinToInterrupt is defined but NOT_AN_INTERRUPT is not.<br>
+/// \version 1.37 2014-10-19
+/// Updated doc for connecting RH_NRF24 to Arduino Mega.<br>
+/// Changes to RHGenericDriver::setHeaderFlags(), so that the default for the clear argument
+/// is now RH_FLAGS_APPLICATION_SPECIFIC, which is less surprising to users.
+/// Testing with the excellent MoteinoMEGA from LowPowerLab
+/// https://lowpowerlab.com/shop/moteinomega with on-board RFM69W.
+/// \version 1.38 2014-12-29
+/// Fixed compile warning on some platforms where RH_RF24::send and RH_RF24::writeTxFifo
+/// did not return a value.<br>
+/// Fixed some more compiler warnings in RH_RF24 on some platforms.<br>
+/// Refactored printRegisters for some radios. Printing to Serial
+/// is now controlled by the definition of RH_HAVE_SERIAL.<br>
+/// Added partial support for ARM M4 w/CMSIS with STM's Hardware Abstraction lib for
+/// Steve Childress.<br>
+/// \version 1.39 2014-12-30
+/// Fix some compiler warnings under IAR.<br>
+/// RH_HAVE_SERIAL and Serial.print calls removed for ATTiny platforms.<br>
+/// \version 1.40 2015-03-09
+/// Added notice about availability on PlatformIO, thanks to Ivan Kravets.<br>
+/// Fixed a problem with RH_NRF24 where short packet lengths would occasionally not be trasmitted
+/// due to a race condition with RH_NRF24_TX_DS. Reported by Mark Fox.<br>
+/// \version 1.41 2015-03-29
+/// RH_RF22, RH_RF24, RH_RF69 and RH_RF95 improved to allow driver.init() to be called multiple
+/// times without reallocating a new interrupt, allowing the driver to be reinitialised
+/// after sleeping or powering down.
+/// \version 1.42 2015-05-17
+/// Added support for RH_NRF24 driver on Raspberry Pi, using BCM2835
+/// library for GPIO pin IO. Contributed by Mike Poublon.<br>
+/// Tested RH_NRF24 module with NRF24L01+PA+LNA SMA Antenna Wireless Transceiver modules
+/// similar to: http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA
+/// works with no software changes. Measured max power output 18dBm.<br>
+/// \version 1.43 2015-08-02
+/// Added RH_NRF51 driver to support Nordic nRF51 family processor with 2.4GHz radio such
+/// as nRF51822, to be built on Arduino 1.6.4 and later. Tested with RedBearLabs nRF51822 board
+/// and BLE Nano kit<br>
+/// \version 1.44 2015-08-08
+/// Fixed errors with compiling on some platforms without serial, such as ATTiny.
+/// Reported by Friedrich Müller.<br>
+/// \version 1.45 2015-08-13
+/// Added support for using RH_Serial on Linux and OSX (new class RHutil/HardwareSerial
+/// encapsulates serial ports on those platforms). Example examples/serial*/* upgraded
+/// to build and run on Linux and OSX using the tools/simBuild builder.
+/// RHMesh, RHRouter and RHReliableDatagram updated so they can use RH_Serial without
+/// polling loops on Linux and OSX for CPU efficiency.<br>
+/// \version 1.46 2015-08-14
+/// Amplified some doc concerning Linux and OSX RH_Serial. Added support for 230400
+/// baud rate in HardwareSerial.<br>
+/// Added sample sketches nrf51_audio_tx and nrf51_audio_rx which show how to
+/// build an audio TX/RX pair with RedBear nRF51822 boards and a SparkFun MCP4725 DAC board.
+/// Uses the built-in ADC of the nRF51822 to sample audio at 5kHz and transmit packets
+/// to the receiver which plays them via the DAC.<br>
+/// \version 1.47 2015-09-18
+/// Removed top level Makefile from distribution: its only used by the developer and
+/// its presence confuses some people.<br>
+/// Fixed a problem with RHReliableDatagram with some versions of Raspberry Pi random() that causes
+/// problems: random(min, max) sometimes exceeds its max limit.
+/// \version 1.48 2015-09-30
+/// Added support for Arduino Zero. Tested on Arduino Zero Pro.
+///
+/// \author Mike McCauley. DO NOT CONTACT THE AUTHOR DIRECTLY. USE THE MAILING LIST GIVEN ABOVE
+
+#ifndef RadioHead_h
+#define RadioHead_h
+
+// Official version numbers are maintained automatically by Makefile:
+#define RH_VERSION_MAJOR 1
+#define RH_VERSION_MINOR 48
+
+// Symbolic names for currently supported platform types
+#define RH_PLATFORM_ARDUINO 1
+#define RH_PLATFORM_MSP430 2
+#define RH_PLATFORM_STM32 3
+#define RH_PLATFORM_GENERIC_AVR8 4
+#define RH_PLATFORM_UNO32 5
+#define RH_PLATFORM_UNIX 6
+#define RH_PLATFORM_STM32STD 7
+#define RH_PLATFORM_STM32F4_HAL 8
+#define RH_PLATFORM_RASPI 9
+#define RH_PLATFORM_NRF51 10
+
+////////////////////////////////////////////////////
+// Select platform automatically, if possible
+#ifndef RH_PLATFORM
+ #if defined(MPIDE)
+ #define RH_PLATFORM RH_PLATFORM_UNO32
+ #elif defined(NRF51)
+ #define RH_PLATFORM RH_PLATFORM_NRF51
+ #elif defined(ARDUINO)
+ #define RH_PLATFORM RH_PLATFORM_ARDUINO
+ #elif defined(__MSP430G2452__) || defined(__MSP430G2553__)
+ #define RH_PLATFORM RH_PLATFORM_MSP430
+ #elif defined(MCU_STM32F103RE)
+ #define RH_PLATFORM RH_PLATFORM_STM32
+ #elif defined(USE_STDPERIPH_DRIVER)
+ #define RH_PLATFORM RH_PLATFORM_STM32STD
+ #elif defined(RASPBERRY_PI)
+ #define RH_PLATFORM RH_PLATFORM_RASPI
+#elif defined(__unix__) // Linux
+ #define RH_PLATFORM RH_PLATFORM_UNIX
+#elif defined(__APPLE__) // OSX
+ #define RH_PLATFORM RH_PLATFORM_UNIX
+#elif defined(TARGET_STM32F407) // Mbed STM32F4
+ #define RH_PLATFORM RH_PLATFORM_MBED
+ #else
+ #error Platform not defined!
+ #endif
+#endif
+
+#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtinyX4__) || defined(__AVR_ATtinyX5__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) || defined(__AVR_ATtinyX313__)
+ #define RH_PLATFORM_ATTINY
+#endif
+
+////////////////////////////////////////////////////
+// Platform specific headers:
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
+ #if (ARDUINO >= 100)
+ #include <Arduino.h>
+ #else
+ #include <wiring.h>
+ #endif
+ #ifdef RH_PLATFORM_ATTINY
+ #warning Arduino TinyCore does not support hardware SPI. Use software SPI instead.
+ #else
+ #include <SPI.h>
+ #define RH_HAVE_HARDWARE_SPI
+ #define RH_HAVE_SERIAL
+ #endif
+
+#elif (RH_PLATFORM == RH_PLATFORM_MSP430) // LaunchPad specific
+ #include "legacymsp430.h"
+ #include "Energia.h"
+ #include <SPI.h>
+ #define RH_HAVE_HARDWARE_SPI
+ #define RH_HAVE_SERIAL
+
+#elif (RH_PLATFORM == RH_PLATFORM_UNO32)
+ #include <WProgram.h>
+ #include <string.h>
+ #include <SPI.h>
+ #define RH_HAVE_HARDWARE_SPI
+ #define memcpy_P memcpy
+ #define RH_HAVE_SERIAL
+
+#elif (RH_PLATFORM == RH_PLATFORM_STM32) // Maple, Flymaple etc
+ #include <wirish.h>
+ #include <stdint.h>
+ #include <string.h>
+ #include <HardwareSPI.h>
+ #define RH_HAVE_HARDWARE_SPI
+ // Defines which timer to use on Maple
+ #define MAPLE_TIMER 1
+ #define PROGMEM
+ #define memcpy_P memcpy
+ #define Serial SerialUSB
+ #define RH_HAVE_SERIAL
+
+#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32 with STM32F4xx_StdPeriph_Driver
+ #include <stm32f4xx.h>
+ #include <wirish.h>
+ #include <stdint.h>
+ #include <string.h>
+ #include <math.h>
+ #include <HardwareSPI.h>
+ #define RH_HAVE_HARDWARE_SPI
+ #define Serial SerialUSB
+ #define RH_HAVE_SERIAL
+
+#elif (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
+ #include <avr/io.h>
+ #include <avr/interrupt.h>
+ #include <util/delay.h>
+ #include <string.h>
+ #include <stdbool.h>
+ #define RH_HAVE_HARDWARE_SPI
+ #include <SPI.h>
+
+// For Steve Childress port to ARM M4 w/CMSIS with STM's Hardware Abstraction lib.
+// See ArduinoWorkarounds.h (not supplied)
+#elif (RH_PLATFORM == RH_PLATFORM_STM32F4_HAL)
+ #include <ArduinoWorkarounds.h>
+ #include <stm32f4xx.h> // Also using ST's CubeMX to generate I/O and CPU setup source code for IAR/EWARM, not GCC ARM.
+ #include <stdint.h>
+ #include <string.h>
+ #include <math.h>
+ #define RH_HAVE_HARDWARE_SPI // using HAL (Hardware Abstraction Libraries from ST along with CMSIS, not arduino libs or pins concept.
+
+#elif (RH_PLATFORM == RH_PLATFORM_RASPI)
+ #define RH_HAVE_HARDWARE_SPI
+ #define RH_HAVE_SERIAL
+ #define PROGMEM
+ #include <RHutil/RasPi.h>
+ #include <string.h>
+ //Define SS for CS0 or pin 24
+ #define SS 8
+
+#elif (RH_PLATFORM == RH_PLATFORM_NRF51)
+ #define RH_HAVE_SERIAL
+ #define PROGMEM
+ #include <Arduino.h>
+
+#elif (RH_PLATFORM == RH_PLATFORM_UNIX)
+ // Simulate the sketch on Linux and OSX
+ #include <RHutil/simulator.h>
+ #define RH_HAVE_SERIAL
+
+#elif (RH_PLATFORM == RH_PLATFORM_MBED)
+ #include <mbed.h>
+ #define RH_HAVE_HARDWARE_SPI
+
+ extern Timer _millisTimer;
+
+ #define PROGMEM
+
+ #define HIGH 1
+ #define LOW 0
+
+ #define millis() _millisTimer.read_ms()
+ #define delay(ms) wait_ms(ms)
+ #define digitalWrite(pin, val) pin = val
+ #define digitalRead(pin) pin
+ #define memcpy_P memcpy
+
+#else
+ #error Platform unknown!
+#endif
+
+
+#if (RH_PLATFORM == RH_PLATFORM_MBED)
+ #define PINS PinName
+#else
+ #define PINS uint8_t
+#endif
+
+////////////////////////////////////////////////////
+// This is an attempt to make a portable atomic block
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
+#if defined(__arm__)
+ #include <RHutil/atomic.h>
+ #else
+ #include <util/atomic.h>
+ #endif
+ #define ATOMIC_BLOCK_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+ #define ATOMIC_BLOCK_END }
+#elif (RH_PLATFORM == RH_PLATFORM_UNO32)
+ #include <peripheral/int.h>
+ #define ATOMIC_BLOCK_START unsigned int __status = INTDisableInterrupts(); {
+ #define ATOMIC_BLOCK_END } INTRestoreInterrupts(__status);
+#else
+ // TO BE DONE:
+ #define ATOMIC_BLOCK_START
+ #define ATOMIC_BLOCK_END
+#endif
+
+////////////////////////////////////////////////////
+// Try to be compatible with systems that support yield() and multitasking
+// instead of spin-loops
+// Recent Arduino IDE or Teensy 3 has yield()
+#if (RH_PLATFORM == RH_PLATFORM_ARDUINO && ARDUINO >= 155 && !defined(RH_PLATFORM_ATTINY)) || (TEENSYDUINO && defined(__MK20DX128__))
+ #define YIELD yield();
+#else
+ #define YIELD
+#endif
+
+////////////////////////////////////////////////////
+// digitalPinToInterrupt is not available prior to Arduino 1.5.6 and 1.0.6
+// See http://arduino.cc/en/Reference/attachInterrupt
+#ifndef NOT_AN_INTERRUPT
+ #define NOT_AN_INTERRUPT -1
+#endif
+#ifndef digitalPinToInterrupt
+ #if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && !defined(__arm__)
+
+ #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+ // Arduino Mega, Mega ADK, Mega Pro
+ // 2->0, 3->1, 21->2, 20->3, 19->4, 18->5
+ #define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT)))
+
+ #elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
+ // Arduino 1284 and 1284P - See Manicbug and Optiboot
+ // 10->0, 11->1, 2->2
+ #define digitalPinToInterrupt(p) ((p) == 10 ? 0 : ((p) == 11 ? 1 : ((p) == 2 ? 2 : NOT_AN_INTERRUPT)))
+
+ #elif defined(__AVR_ATmega32U4__)
+ // Leonardo, Yun, Micro, Pro Micro, Flora, Esplora
+ // 3->0, 2->1, 0->2, 1->3, 7->4
+ #define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT)))))
+
+ #else
+ // All other arduino except Due:
+ // Serial Arduino, Extreme, NG, BT, Uno, Diecimila, Duemilanove, Nano, Menta, Pro, Mini 04, Fio, LilyPad, Ethernet etc
+ // 2->0, 3->1
+ #define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT))
+
+ #endif
+
+ #elif (RH_PLATFORM == RH_PLATFORM_UNO32)
+ #define digitalPinToInterrupt(p) ((p) == 38 ? 0 : ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 735 ? 4 : NOT_AN_INTERRUPT)))))
+
+ #else
+ // Everything else (including Due and Teensy) interrupt number the same as the interrupt pin number
+ #define digitalPinToInterrupt(p) (p)
+ #endif
+#endif
+
+// Slave select pin, some platforms such as ATTiny do not define it.
+#ifndef SS
+ #define SS 10
+#endif
+
+// These defs cause trouble on some versions of Arduino
+#undef abs
+#undef round
+#undef double
+
+// This is the address that indicates a broadcast
+#define RH_BROADCAST_ADDRESS 0xff
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/radio_config_Si4460.h Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,606 @@
+/*! @file radio_config.h
+ * @brief This file contains the automatically generated
+ * configurations.
+ *
+ * @n WDS GUI Version: 3.2.6.0
+ * @n Device: Si4460 Rev.: B1
+ *
+ * @b COPYRIGHT
+ * @n Silicon Laboratories Confidential
+ * @n Copyright 2013 Silicon Laboratories, Inc.
+ * @n http://www.silabs.com
+ */
+
+#ifndef RADIO_CONFIG_H_
+#define RADIO_CONFIG_H_
+
+// USER DEFINED PARAMETERS
+// Define your own parameters here
+
+// INPUT DATA
+/*
+// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0
+// MOD_type: 3 Rsymb(sps): 50000 Fdev(Hz): 100000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 2
+// RF Freq.(MHz): 434 API_TC: 31 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1
+//
+// # WB filter 2 (BW = 274.83 kHz); NB-filter 2 (BW = 274.83 kHz)
+//
+// Modulation index: 4
+*/
+
+
+// CONFIGURATION PARAMETERS
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ {30000000L}
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER {0x00}
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH {0x07}
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP {0x03}
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET {0xF000}
+#define RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD {0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5}
+
+
+// CONFIGURATION COMMANDS
+
+/*
+// Command: RF_POWER_UP
+// Description: Command to power-up the device and select the operational mode and functionality.
+*/
+#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80
+
+/*
+// Command: RF_GPIO_PIN_CFG
+// Description: Configures the GPIO pins.
+*/
+#define RF_GPIO_PIN_CFG 0x13, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_GLOBAL_XO_TUNE_1
+// Number of properties: 1
+// Group ID: 0x00
+// Start ID: 0x00
+// Default values: 0x40,
+// Descriptions:
+// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator.
+*/
+#define RF_GLOBAL_XO_TUNE_1 0x11, 0x00, 0x01, 0x00, 0x52
+
+/*
+// Set properties: RF_GLOBAL_CONFIG_1
+// Number of properties: 1
+// Group ID: 0x00
+// Start ID: 0x03
+// Default values: 0x20,
+// Descriptions:
+// GLOBAL_CONFIG - Global configuration settings.
+*/
+#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60
+
+/*
+// Set properties: RF_INT_CTL_ENABLE_2
+// Number of properties: 2
+// Group ID: 0x01
+// Start ID: 0x00
+// Default values: 0x04, 0x00,
+// Descriptions:
+// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin.
+// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin.
+*/
+#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x38
+
+/*
+// Set properties: RF_FRR_CTL_A_MODE_4
+// Number of properties: 4
+// Group ID: 0x02
+// Start ID: 0x00
+// Default values: 0x01, 0x02, 0x09, 0x00,
+// Descriptions:
+// FRR_CTL_A_MODE - Fast Response Register A Configuration.
+// FRR_CTL_B_MODE - Fast Response Register B Configuration.
+// FRR_CTL_C_MODE - Fast Response Register C Configuration.
+// FRR_CTL_D_MODE - Fast Response Register D Configuration.
+*/
+#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PREAMBLE_TX_LENGTH_9
+// Number of properties: 9
+// Group ID: 0x10
+// Start ID: 0x00
+// Default values: 0x08, 0x14, 0x00, 0x0F, 0x21, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PREAMBLE_TX_LENGTH - Configure length of TX Preamble.
+// PREAMBLE_CONFIG_STD_1 - Configuration of reception of a packet with a Standard Preamble pattern.
+// PREAMBLE_CONFIG_NSTD - Configuration of transmission/reception of a packet with a Non-Standard Preamble pattern.
+// PREAMBLE_CONFIG_STD_2 - Configuration of timeout periods during reception of a packet with Standard Preamble pattern.
+// PREAMBLE_CONFIG - General configuration bits for the Preamble field.
+// PREAMBLE_PATTERN_31_24 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_23_16 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_15_8 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_7_0 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+*/
+#define RF_PREAMBLE_TX_LENGTH_9 0x11, 0x10, 0x09, 0x00, 0x08, 0x14, 0x00, 0x0F, 0x31, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_SYNC_CONFIG_5
+// Number of properties: 5
+// Group ID: 0x11
+// Start ID: 0x00
+// Default values: 0x01, 0x2D, 0xD4, 0x2D, 0xD4,
+// Descriptions:
+// SYNC_CONFIG - Sync Word configuration bits.
+// SYNC_BITS_31_24 - Sync word.
+// SYNC_BITS_23_16 - Sync word.
+// SYNC_BITS_15_8 - Sync word.
+// SYNC_BITS_7_0 - Sync word.
+*/
+#define RF_SYNC_CONFIG_5 0x11, 0x11, 0x05, 0x00, 0x01, 0xB4, 0x2B, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_CRC_CONFIG_1
+// Number of properties: 1
+// Group ID: 0x12
+// Start ID: 0x00
+// Default values: 0x00,
+// Descriptions:
+// PKT_CRC_CONFIG - Select a CRC polynomial and seed.
+*/
+#define RF_PKT_CRC_CONFIG_1 0x11, 0x12, 0x01, 0x00, 0x80
+
+/*
+// Set properties: RF_PKT_WHT_SEED_15_8_4
+// Number of properties: 4
+// Group ID: 0x12
+// Start ID: 0x03
+// Default values: 0xFF, 0xFF, 0x00, 0x00,
+// Descriptions:
+// PKT_WHT_SEED_15_8 - 16-bit seed value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_SEED_7_0 - 16-bit seed value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_BIT_NUM - Selects which bit of the LFSR (used to generate the PN / data whitening sequence) is used as the output bit for data scrambling.
+// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet.
+*/
+#define RF_PKT_WHT_SEED_15_8_4 0x11, 0x12, 0x04, 0x03, 0xFF, 0xFF, 0x00, 0x02
+
+/*
+// Set properties: RF_PKT_LEN_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x08
+// Default values: 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_LEN - Configuration bits for reception of a variable length packet.
+// PKT_LEN_FIELD_SOURCE - Field number containing the received packet length byte(s).
+// PKT_LEN_ADJUST - Provides for adjustment/offset of the received packet length value (in order to accommodate a variety of methods of defining total packet length).
+// PKT_TX_THRESHOLD - TX FIFO almost empty threshold.
+// PKT_RX_THRESHOLD - RX FIFO Almost Full threshold.
+// PKT_FIELD_1_LENGTH_12_8 - Unsigned 13-bit Field 1 length value.
+// PKT_FIELD_1_LENGTH_7_0 - Unsigned 13-bit Field 1 length value.
+// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1.
+// PKT_FIELD_1_CRC_CONFIG - Configuration of CRC control bits across Field 1.
+// PKT_FIELD_2_LENGTH_12_8 - Unsigned 13-bit Field 2 length value.
+// PKT_FIELD_2_LENGTH_7_0 - Unsigned 13-bit Field 2 length value.
+// PKT_FIELD_2_CONFIG - General data processing and packet configuration bits for Field 2.
+*/
+#define RF_PKT_LEN_12 0x11, 0x12, 0x0C, 0x08, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_FIELD_2_CRC_CONFIG_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x14
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_FIELD_2_CRC_CONFIG - Configuration of CRC control bits across Field 2.
+// PKT_FIELD_3_LENGTH_12_8 - Unsigned 13-bit Field 3 length value.
+// PKT_FIELD_3_LENGTH_7_0 - Unsigned 13-bit Field 3 length value.
+// PKT_FIELD_3_CONFIG - General data processing and packet configuration bits for Field 3.
+// PKT_FIELD_3_CRC_CONFIG - Configuration of CRC control bits across Field 3.
+// PKT_FIELD_4_LENGTH_12_8 - Unsigned 13-bit Field 4 length value.
+// PKT_FIELD_4_LENGTH_7_0 - Unsigned 13-bit Field 4 length value.
+// PKT_FIELD_4_CONFIG - General data processing and packet configuration bits for Field 4.
+// PKT_FIELD_4_CRC_CONFIG - Configuration of CRC control bits across Field 4.
+// PKT_FIELD_5_LENGTH_12_8 - Unsigned 13-bit Field 5 length value.
+// PKT_FIELD_5_LENGTH_7_0 - Unsigned 13-bit Field 5 length value.
+// PKT_FIELD_5_CONFIG - General data processing and packet configuration bits for Field 5.
+*/
+#define RF_PKT_FIELD_2_CRC_CONFIG_12 0x11, 0x12, 0x0C, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_FIELD_5_CRC_CONFIG_1
+// Number of properties: 1
+// Group ID: 0x12
+// Start ID: 0x20
+// Default values: 0x00,
+// Descriptions:
+// PKT_FIELD_5_CRC_CONFIG - Configuration of CRC control bits across Field 5.
+*/
+#define RF_PKT_FIELD_5_CRC_CONFIG_1 0x11, 0x12, 0x01, 0x20, 0x00
+
+/*
+// Set properties: RF_MODEM_MOD_TYPE_12
+// Number of properties: 12
+// Group ID: 0x20
+// Start ID: 0x00
+// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06,
+// Descriptions:
+// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation.
+// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits.
+// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer.
+// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word.
+// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word.
+*/
+#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x0F, 0x42, 0x40, 0x09, 0xC9, 0xC3, 0x80, 0x00, 0x1B
+
+/*
+// Set properties: RF_MODEM_FREQ_DEV_0_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x0C
+// Default values: 0xD3,
+// Descriptions:
+// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word.
+*/
+#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0x4F
+
+/*
+// Set properties: RF_MODEM_TX_RAMP_DELAY_8
+// Number of properties: 8
+// Group ID: 0x20
+// Start ID: 0x18
+// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20,
+// Descriptions:
+// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting.
+// MODEM_MDM_CTRL - MDM control.
+// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation.
+// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number).
+// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number).
+// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number).
+// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter.
+// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter.
+*/
+#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x00, 0x10
+
+/*
+// Set properties: RF_MODEM_BCR_OSR_1_9
+// Number of properties: 9
+// Group ID: 0x20
+// Start ID: 0x22
+// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0,
+// Descriptions:
+// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number).
+// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number).
+// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value.
+// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value.
+// MODEM_BCR_GEAR - RX BCR loop gear control.
+// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop.
+*/
+#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x00, 0xC8, 0x02, 0x8F, 0x5C, 0x01, 0x48, 0x02, 0xC2
+
+/*
+// Set properties: RF_MODEM_AFC_GEAR_7
+// Number of properties: 7
+// Group ID: 0x20
+// Start ID: 0x2C
+// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0,
+// Descriptions:
+// MODEM_AFC_GEAR - RX AFC loop gear control.
+// MODEM_AFC_WAIT - RX AFC loop wait time control.
+// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality.
+// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality.
+// MODEM_AFC_LIMITER_1 - Set the AFC limiter value.
+// MODEM_AFC_LIMITER_0 - Set the AFC limiter value.
+// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits.
+*/
+#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x92, 0x0A, 0x46, 0x80
+
+/*
+// Set properties: RF_MODEM_AGC_CONTROL_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x35
+// Default values: 0xE0,
+// Descriptions:
+// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain.
+*/
+#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2
+
+/*
+// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9
+// Number of properties: 9
+// Group ID: 0x20
+// Start ID: 0x38
+// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B,
+// Descriptions:
+// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm.
+// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors.
+// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors.
+// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression.
+// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression.
+// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold.
+// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold.
+// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code.
+// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector.
+*/
+#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0x2C, 0x2C, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x29
+
+/*
+// Set properties: RF_MODEM_OOK_CNT1_11
+// Number of properties: 11
+// Group ID: 0x20
+// Start ID: 0x42
+// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, 0xFF, 0x0C, 0x01,
+// Descriptions:
+// MODEM_OOK_CNT1 - OOK control.
+// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator.
+// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors.
+// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode.
+// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold.
+// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold.
+// MODEM_ANT_DIV_MODE - Antenna diversity mode settings.
+// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm.
+// MODEM_RSSI_THRESH - Configures the RSSI threshold.
+// MODEM_RSSI_JUMP_THRESH - Configures the RSSI Jump Detection threshold.
+// MODEM_RSSI_CONTROL - Control of the averaging modes and latching time for reporting RSSI value(s).
+*/
+#define RF_MODEM_OOK_CNT1_11 0x11, 0x20, 0x0B, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x01, 0x7F, 0x01, 0x80, 0xFF, 0x0C, 0x02
+
+/*
+// Set properties: RF_MODEM_RSSI_COMP_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x4E
+// Default values: 0x40,
+// Descriptions:
+// MODEM_RSSI_COMP - RSSI compensation value.
+*/
+#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40
+
+/*
+// Set properties: RF_MODEM_CLKGEN_BAND_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x51
+// Default values: 0x08,
+// Descriptions:
+// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band.
+*/
+#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x0A
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x00
+// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01,
+// Descriptions:
+// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x0C
+// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5,
+// Descriptions:
+// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x18
+// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00,
+// Descriptions:
+// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00
+
+/*
+// Set properties: RF_PA_MODE_4
+// Number of properties: 4
+// Group ID: 0x22
+// Start ID: 0x00
+// Default values: 0x08, 0x7F, 0x00, 0x5D,
+// Descriptions:
+// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size).
+// PA_PWR_LVL - Configuration of PA output power level.
+// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source.
+// PA_TC - Configuration of PA ramping parameters.
+*/
+#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x18, 0x01, 0xC0, 0x3F
+
+/*
+// Set properties: RF_SYNTH_PFDCP_CPFF_7
+// Number of properties: 7
+// Group ID: 0x23
+// Start ID: 0x00
+// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03,
+// Descriptions:
+// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection.
+// SYNTH_PFDCP_CPINT - Integration charge pump current selection.
+// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path.
+// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter.
+// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter.
+// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter.
+// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter.
+*/
+#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03
+
+/*
+// Set properties: RF_MATCH_VALUE_1_12
+// Number of properties: 12
+// Group ID: 0x30
+// Start ID: 0x00
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// MATCH_VALUE_1 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 1 value with the received Match 1 byte.
+// MATCH_MASK_1 - Mask value to be logically AND-ed (bit-wise) with the Match 1 byte.
+// MATCH_CTRL_1 - Enable for Packet Match functionality, and configuration of Match Byte 1.
+// MATCH_VALUE_2 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 2 value with the received Match 2 byte.
+// MATCH_MASK_2 - Mask value to be logically AND-ed (bit-wise) with the Match 2 byte.
+// MATCH_CTRL_2 - Configuration of Match Byte 2.
+// MATCH_VALUE_3 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 3 value with the received Match 3 byte.
+// MATCH_MASK_3 - Mask value to be logically AND-ed (bit-wise) with the Match 3 byte.
+// MATCH_CTRL_3 - Configuration of Match Byte 3.
+// MATCH_VALUE_4 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 4 value with the received Match 4 byte.
+// MATCH_MASK_4 - Mask value to be logically AND-ed (bit-wise) with the Match 4 byte.
+// MATCH_CTRL_4 - Configuration of Match Byte 4.
+*/
+#define RF_MATCH_VALUE_1_12 0x11, 0x30, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_FREQ_CONTROL_INTE_8
+// Number of properties: 8
+// Group ID: 0x40
+// Start ID: 0x00
+// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF,
+// Descriptions:
+// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number.
+// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number.
+// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number.
+// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number.
+// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size.
+// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size.
+// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration.
+// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode.
+*/
+#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x38, 0x0E, 0xEE, 0xEE, 0x44, 0x44, 0x20, 0xFE
+
+
+// AUTOMATICALLY GENERATED CODE!
+// DO NOT EDIT/MODIFY BELOW THIS LINE!
+// --------------------------------------------
+
+#ifndef FIRMWARE_LOAD_COMPILE
+#define RADIO_CONFIGURATION_DATA_ARRAY { \
+ 0x07, RF_POWER_UP, \
+ 0x08, RF_GPIO_PIN_CFG, \
+ 0x05, RF_GLOBAL_XO_TUNE_1, \
+ 0x05, RF_GLOBAL_CONFIG_1, \
+ 0x06, RF_INT_CTL_ENABLE_2, \
+ 0x08, RF_FRR_CTL_A_MODE_4, \
+ 0x0D, RF_PREAMBLE_TX_LENGTH_9, \
+ 0x09, RF_SYNC_CONFIG_5, \
+ 0x05, RF_PKT_CRC_CONFIG_1, \
+ 0x08, RF_PKT_WHT_SEED_15_8_4, \
+ 0x10, RF_PKT_LEN_12, \
+ 0x10, RF_PKT_FIELD_2_CRC_CONFIG_12, \
+ 0x05, RF_PKT_FIELD_5_CRC_CONFIG_1, \
+ 0x10, RF_MODEM_MOD_TYPE_12, \
+ 0x05, RF_MODEM_FREQ_DEV_0_1, \
+ 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \
+ 0x0D, RF_MODEM_BCR_OSR_1_9, \
+ 0x0B, RF_MODEM_AFC_GEAR_7, \
+ 0x05, RF_MODEM_AGC_CONTROL_1, \
+ 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \
+ 0x0F, RF_MODEM_OOK_CNT1_11, \
+ 0x05, RF_MODEM_RSSI_COMP_1, \
+ 0x05, RF_MODEM_CLKGEN_BAND_1, \
+ 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \
+ 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \
+ 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \
+ 0x08, RF_PA_MODE_4, \
+ 0x0B, RF_SYNTH_PFDCP_CPFF_7, \
+ 0x10, RF_MATCH_VALUE_1_12, \
+ 0x0C, RF_FREQ_CONTROL_INTE_8, \
+ 0x00 \
+ }
+#else
+#define RADIO_CONFIGURATION_DATA_ARRAY { 0 }
+#endif
+
+// DEFAULT VALUES FOR CONFIGURATION PARAMETERS
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000
+#define RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD_DEFAULT 0x42, 0x55, 0x54, 0x54, 0x4F, 0x4E, 0x31 // BUTTON1
+
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { }
+
+#ifndef RADIO_CONFIGURATION_DATA_ARRAY
+#error "This property must be defined!"
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ { RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT }
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER { RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT }
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH { RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT }
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP { RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT }
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET { RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT }
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD
+#define RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD { RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD_DEFAULT }
+#endif
+
+#define RADIO_CONFIGURATION_DATA { \
+ Radio_Configuration_Data_Array, \
+ RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \
+ RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \
+ RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \
+ RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET, \
+ RADIO_CONFIGURATION_DATA_CUSTOM_PAYLOAD \
+ }
+
+#endif /* RADIO_CONFIG_H_ */