Port of RadioHead version 1.48 to mbed. It is a little messy and only works for SPI at this time.
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
diff -r 000000000000 -r ab4e012489ef RHCRC.cpp --- /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; +} + +
diff -r 000000000000 -r ab4e012489ef RHCRC.h --- /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
diff -r 000000000000 -r ab4e012489ef RHDatagram.cpp --- /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(); +} + + +
diff -r 000000000000 -r ab4e012489ef RHDatagram.h --- /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
diff -r 000000000000 -r ab4e012489ef RHGenericDriver.cpp --- /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
diff -r 000000000000 -r ab4e012489ef RHGenericDriver.h --- /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
diff -r 000000000000 -r ab4e012489ef RHGenericSPI.cpp --- /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; +} +
diff -r 000000000000 -r ab4e012489ef RHGenericSPI.h --- /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
diff -r 000000000000 -r ab4e012489ef RHHardwareSPI.cpp --- /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 +
diff -r 000000000000 -r ab4e012489ef RHHardwareSPI.h --- /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
diff -r 000000000000 -r ab4e012489ef RHMesh.cpp --- /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; +} + + +
diff -r 000000000000 -r ab4e012489ef RHMesh.h --- /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 +
diff -r 000000000000 -r ab4e012489ef RHNRFSPIDriver.cpp --- /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; +} + + +
diff -r 000000000000 -r ab4e012489ef RHNRFSPIDriver.h --- /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
diff -r 000000000000 -r ab4e012489ef RHReliableDatagram.cpp --- /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(); +} +
diff -r 000000000000 -r ab4e012489ef RHReliableDatagram.h --- /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 +
diff -r 000000000000 -r ab4e012489ef RHRouter.cpp --- /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; +} +
diff -r 000000000000 -r ab4e012489ef RHRouter.h --- /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 +
diff -r 000000000000 -r ab4e012489ef RHSPIDriver.cpp --- /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; +} + + +
diff -r 000000000000 -r ab4e012489ef RHSPIDriver.h --- /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
diff -r 000000000000 -r ab4e012489ef RHTcpProtocol.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_NRF24.cpp --- /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; +}
diff -r 000000000000 -r ab4e012489ef RH_NRF24.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_NRF51.cpp --- /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
diff -r 000000000000 -r ab4e012489ef RH_NRF51.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_NRF905.cpp --- /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; +}
diff -r 000000000000 -r ab4e012489ef RH_NRF905.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_RF22.cpp --- /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 + } +} +
diff -r 000000000000 -r ab4e012489ef RH_RF22.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_RF24.cpp --- /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; +}
diff -r 000000000000 -r ab4e012489ef RH_RF24.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_RF69.cpp --- /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; +}
diff -r 000000000000 -r ab4e012489ef RH_RF69.h --- /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
diff -r 000000000000 -r ab4e012489ef RH_RF95.cpp --- /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); +} +
diff -r 000000000000 -r ab4e012489ef RH_RF95.h --- /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 +
diff -r 000000000000 -r ab4e012489ef RH_TCP.cpp --- /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
diff -r 000000000000 -r ab4e012489ef RH_TCP.h --- /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
diff -r 000000000000 -r ab4e012489ef RadioHead.h --- /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
diff -r 000000000000 -r ab4e012489ef radio_config_Si4460.h --- /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_ */