Revision 0:e69d086cb053, committed 2017-06-11
- Comitter:
- danjulio
- Date:
- Sun Jun 11 04:05:05 2017 +0000
- Commit message:
- Initial commit of minimally ported Radiohead library using swspi
Changed in this revision
diff -r 000000000000 -r e69d086cb053 RHGenericDriver.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericDriver.cpp Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,225 @@
+// RHGenericDriver.cpp
+//
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHGenericDriver.cpp,v 1.21 2017/03/04 00:59:41 mikem Exp $
+//
+// Ported to mbed - support only a single radio - Dan Julio - 5/2017
+//
+
+#include <RHGenericDriver.h>
+
+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),
+ _cad_timeout(0)
+{
+}
+
+bool RHGenericDriver::init()
+{
+ return true;
+}
+
+// Blocks until a valid message is received
+void RHGenericDriver::waitAvailable()
+{
+ while (!available())
+ Thread::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)
+{
+ _waitTimer.reset();
+ _waitTimer.start();
+
+ while (_waitTimer.read_ms() < timeout)
+ {
+ if (available())
+ {
+ _waitTimer.stop();
+ return true;
+ }
+ Thread::yield();
+ }
+ _waitTimer.stop();
+ return false;
+}
+
+bool RHGenericDriver::waitPacketSent()
+{
+ while (_mode == RHModeTx)
+ Thread::yield(); // Wait for any previous transmit to finish
+ return true;
+}
+
+bool RHGenericDriver::waitPacketSent(uint16_t timeout)
+{
+ _waitTimer.reset();
+ _waitTimer.start();
+ while (_waitTimer.read_ms() < timeout)
+ {
+ if (_mode != RHModeTx) // Any previous transmit finished?
+ {
+ _waitTimer.stop();
+ return true;
+ }
+ Thread::yield();
+ }
+ _waitTimer.stop();
+ return false;
+}
+
+// Wait until no channel activity detected or timeout
+bool RHGenericDriver::waitCAD()
+{
+ if (!_cad_timeout)
+ return true;
+
+ // Wait for any channel activity to finish or timeout
+ // Sophisticated DCF function...
+ // DCF : BackoffTime = random() x aSlotTime
+ // 100 - 1000 ms
+ // 10 sec timeout
+ _waitTimer.reset();
+ _waitTimer.start();
+ while (isChannelActive())
+ {
+ if (_waitTimer.read_ms() > _cad_timeout)
+ {
+ _waitTimer.stop();
+ return false;
+ }
+
+ wait_ms(100);
+ }
+
+ _waitTimer.stop();
+ return true;
+}
+
+// subclasses are expected to override if CAD is available for that radio
+bool RHGenericDriver::isChannelActive()
+{
+ 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;
+
+ printf("%s", prompt);
+ for (i = 0; i < len; i++)
+ {
+ if (i % 16 == 15)
+ printf("0x%x\n", buf[i]);
+ else
+ {
+ printf("0x%x ", buf[i]);
+ }
+ }
+ printf("\n");
+}
+
+uint16_t RHGenericDriver::rxBad()
+{
+ return _rxBad;
+}
+
+uint16_t RHGenericDriver::rxGood()
+{
+ return _rxGood;
+}
+
+uint16_t RHGenericDriver::txGood()
+{
+ return _txGood;
+}
+
+void RHGenericDriver::setCADTimeout(unsigned long cad_timeout)
+{
+ _cad_timeout = cad_timeout;
+}
+
diff -r 000000000000 -r e69d086cb053 RHGenericDriver.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RHGenericDriver.h Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,316 @@
+// RHGenericDriver.h
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2014 Mike McCauley
+// $Id: RHGenericDriver.h,v 1.19 2017/03/08 09:30:47 mikem Exp mikem $
+//
+// Ported to mbed - support only a single radio - Dan Julio - 5/2017
+//
+
+#ifndef RHGenericDriver_h
+#define RHGenericDriver_h
+
+#include "RadioHead.h"
+#include "mbed.h"
+#include "max32630fthr.h"
+#include <stdint.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
+
+// Default timeout for waitCAD() in ms
+#define RH_CAD_DEFAULT_TIMEOUT 10000
+
+/////////////////////////////////////////////////////////////////////
+/// \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.
+ RHModeCad ///< Transport is in the process of detecting channel activity (if supported)
+ } 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 optionally waits for Channel Activity Detection (CAD)
+ /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD().
+ /// 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)
+ /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting.
+ /// \return true if the message length was valid and it was correctly queued for transmit. Return false
+ /// if CAD was requested and the CAD timeout timed out before clear channel was detected.
+ 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 radio 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);
+
+ // Bent G Christensen (bentor@gmail.com), 08/15/2016
+ /// Channel Activity Detection (CAD).
+ /// Blocks until channel activity is finished or CAD timeout occurs.
+ /// Uses the radio's CAD function (if supported) to detect channel activity.
+ /// Implements random delays of 100 to 1000ms while activity is detected and until timeout.
+ /// Caution: the random() function is not seeded. If you want non-deterministic behaviour, consider
+ /// using something like randomSeed(analogRead(A0)); in your sketch.
+ /// Permits the implementation of listen-before-talk mechanism (Collision Avoidance).
+ /// Calls the isChannelActive() member function for the radio (if supported)
+ /// to determine if the channel is active. If the radio does not support isChannelActive(),
+ /// always returns true immediately
+ /// \return true if the radio-specific CAD (as returned by isChannelActive())
+ /// shows the channel is clear within the timeout period (or the timeout period is 0), else returns false.
+ virtual bool waitCAD();
+
+ /// Sets the Channel Activity Detection timeout in milliseconds to be used by waitCAD().
+ /// The default is 0, which means do not wait for CAD detection.
+ /// CAD detection depends on support for isChannelActive() by your particular radio.
+ void setCADTimeout(unsigned long cad_timeout);
+
+ /// Determine if the currently selected radio channel is active.
+ /// This is expected to be subclassed by specific radios to implement their Channel Activity Detection
+ /// if supported. If the radio does not support CAD, returns true immediately. If a RadioHead radio
+ /// supports isChannelActive() it will be documented in the radio specific documentation.
+ /// This is called automatically by waitCAD().
+ /// \return true if the radio-specific CAD (as returned by override of isChannelActive()) shows the
+ /// current radio channel as active, else false. If there is no radio-specific CAD, returns false.
+ virtual bool isChannelActive();
+
+ /// 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, resulting 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;
+
+ /// Channel activity detected
+ volatile bool _cad;
+
+ /// Channel activity timeout in ms
+ unsigned int _cad_timeout;
+
+private:
+
+ Timer _waitTimer;
+
+};
+
+
+#endif
diff -r 000000000000 -r e69d086cb053 RH_RF95.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF95.cpp Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,399 @@
+// RH_RF95.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF95.cpp,v 1.14 2017/03/04 00:59:41 mikem Exp $
+//
+// Ported to mbed - support only a single radio - Dan Julio - 5/2017
+//
+
+#include <RH_RF95.h>
+
+// These are indexed by the values of ModemConfigChoice
+// Stored in flash (program) memory to save SRAM
+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(swspi& spi, int ssNum)
+ :
+ _spi(spi),
+ _ssn(ssNum),
+ _rxBufValid(0)
+{
+}
+
+bool RH_RF95::init()
+{
+ // Set sleep mode, so we can also set LORA mode:
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE, 0, _ssn);
+ wait_ms(10); // Wait for sleep mode to take over from say, CAD
+ // Check we are in sleep mode, with LORA set
+ if (_spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE))
+ {
+// printf("REG_01_OP_MODE: 0x%2x\n", _spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn));
+ return false; // No device present?
+ }
+
+ // 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
+ _spi.spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0, 0, _ssn);
+
+ // 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 = _spi.spiRead(RH_RF95_REG_12_IRQ_FLAGS, 0, _ssn);
+ 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 = _spi.spiRead(RH_RF95_REG_13_RX_NB_BYTES, 0, _ssn);
+
+ // Reset the fifo read ptr to the beginning of the packet
+ _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, _spi.spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR, 0, _ssn), 0, _ssn);
+ _spi.spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len, 0, _ssn);
+ _bufLen = len;
+ _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags
+
+ // Remember the last signal to noise ratio, LORA mode
+ // Per page 111, SX1276/77/78/79 datasheet
+ _lastSNR = (int8_t)_spi.spiRead(RH_RF95_REG_19_PKT_SNR_VALUE, 0, _ssn) / 4;
+
+ // Remember the RSSI of this packet, LORA mode
+ // this is according to the doc, but is it really correct?
+ // weakest receiveable signals are reported RSSI at about -66
+ _lastRssi = _spi.spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE, 0, _ssn);
+ // Adjust the RSSI, datasheet page 87
+ if (_lastSNR < 0)
+ _lastRssi = _lastRssi + _lastSNR;
+ else
+ _lastRssi = (int)_lastRssi * 16 / 15;
+ if (_usingHFport)
+ _lastRssi -= 157;
+ else
+ _lastRssi -= 164;
+
+ // We have received a message.
+ validateRxBuf();
+ if (_rxBufValid)
+ setModeIdle(); // Got one
+ }
+ else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE)
+ {
+ _txGood++;
+ setModeIdle();
+ }
+ else if (_mode == RHModeCad && irq_flags & RH_RF95_CAD_DONE)
+ {
+ _cad = irq_flags & RH_RF95_CAD_DETECTED;
+ setModeIdle();
+ }
+
+ _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags
+}
+
+// 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()
+{
+ _rxBufValid = false;
+ _bufLen = 0;
+}
+
+bool RH_RF95::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_RF95_HEADER_LEN)
+ *len = _bufLen-RH_RF95_HEADER_LEN;
+ memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len);
+ }
+ 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();
+
+ if (!waitCAD())
+ return false; // Check channel activity
+
+ // Position at the beginning of the FIFO
+ _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0, 0, _ssn);
+ // The headers
+ _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags, 0, _ssn);
+ // The message data
+ _spi.spiBurstWrite(RH_RF95_REG_00_FIFO, data, len, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN, 0, _ssn);
+
+ setModeTx(); // Start the transmitter
+ // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
+ return true;
+}
+
+bool RH_RF95::printRegisters()
+{
+ 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++)
+ {
+ printf("0x%x: 0x%x\n\r", registers[i], _spi.spiRead(registers[i], 0, _ssn));
+ }
+ 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.0f) / RH_RF95_FSTEP;
+ _spi.spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff, 0, _ssn);
+ _usingHFport = (centre >= 779.0f);
+
+ return true;
+}
+
+void RH_RF95::setModeIdle()
+{
+ if (_mode != RHModeIdle)
+ {
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY, 0, _ssn);
+ _mode = RHModeIdle;
+ }
+}
+
+bool RH_RF95::sleep()
+{
+ if (_mode != RHModeSleep)
+ {
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP, 0, _ssn);
+ _mode = RHModeSleep;
+ }
+ return true;
+}
+
+void RH_RF95::setModeRx()
+{
+ if (_mode != RHModeRx)
+ {
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00, 0, _ssn); // Interrupt on RxDone
+ _mode = RHModeRx;
+ }
+}
+
+void RH_RF95::setModeTx()
+{
+ if (_mode != RHModeTx)
+ {
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40, 0, _ssn); // Interrupt on TxDone
+ _mode = RHModeTx;
+ }
+}
+
+void RH_RF95::setTxPower(int8_t power, bool useRFO)
+{
+ // Sigh, different behaviours depending on whther the module use PA_BOOST or the RFO pin
+ // for the transmitter output
+ if (useRFO)
+ {
+ if (power > 14)
+ power = 14;
+ if (power < -1)
+ power = -1;
+ _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_MAX_POWER | (power + 1), 0, _ssn);
+ }
+ else
+ {
+ 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)
+ {
+ _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE, 0, _ssn);
+ power -= 3;
+ }
+ else
+ {
+ _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE, 0, _ssn);
+ }
+
+ // 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
+ _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5), 0, _ssn);
+ }
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF95::setModemRegisters(const ModemConfig* config)
+{
+ _spi.spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26, 0, _ssn);
+}
+
+// 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(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig));
+ setModemRegisters(&cfg);
+
+ return true;
+}
+
+void RH_RF95::setPreambleLength(uint16_t bytes)
+{
+ _spi.spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff, 0, _ssn);
+}
+
+bool RH_RF95::isChannelActive()
+{
+ // Set mode RHModeCad
+ if (_mode != RHModeCad)
+ {
+ _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_CAD, 0, _ssn);
+ _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x80, 0, _ssn); // Interrupt on CadDone
+ _mode = RHModeCad;
+ }
+
+ while (_mode == RHModeCad)
+ Thread::yield();
+
+ return _cad;
+}
+
+void RH_RF95::enableTCXO()
+{
+ while ((_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) & RH_RF95_TCXO_TCXO_INPUT_ON) != RH_RF95_TCXO_TCXO_INPUT_ON)
+ {
+ sleep();
+ _spi.spiWrite(RH_RF95_REG_4B_TCXO, (_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) | RH_RF95_TCXO_TCXO_INPUT_ON), 0, _ssn);
+ }
+}
+
+// From section 4.1.5 of SX1276/77/78/79
+// Ferror = FreqError * 2**24 * BW / Fxtal / 500
+int RH_RF95::frequencyError()
+{
+ int32_t freqerror = 0;
+
+ // Convert 2.5 bytes (5 nibbles, 20 bits) to 32 bit signed int
+ freqerror = _spi.spiRead(RH_RF95_REG_28_FEI_MSB, 0, _ssn) << 16;
+ freqerror |= _spi.spiRead(RH_RF95_REG_29_FEI_MID, 0, _ssn) << 8;
+ freqerror |= _spi.spiRead(RH_RF95_REG_2A_FEI_LSB, 0, _ssn);
+ // Sign extension into top 3 nibbles
+ if (freqerror & 0x80000)
+ freqerror |= 0xfff00000;
+
+ int error = 0; // In hertz
+ float bw_tab[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250, 500};
+ uint8_t bwindex = _spi.spiRead(RH_RF95_REG_1D_MODEM_CONFIG1, 0, _ssn) >> 4;
+ if (bwindex < (sizeof(bw_tab) / sizeof(float)))
+ error = (float)freqerror * bw_tab[bwindex] * ((float)(1L << 24) / (float)RH_RF95_FXOSC / 500.0f);
+ // else not defined
+
+ return error;
+}
+
+int RH_RF95::lastSNR()
+{
+ return _lastSNR;
+}
diff -r 000000000000 -r e69d086cb053 RH_RF95.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF95.h Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,792 @@
+// 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.16 2017/03/04 00:59:41 mikem Exp $
+//
+// Ported to mbed - support only a single radio - Dan Julio - 5/2017
+//
+
+#ifndef RH_RF95_h
+#define RH_RF95_h
+
+#include <RHGenericDriver.h>
+#include "swspi.h"
+
+// 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_27_PPM_CORRECTION 0x27
+#define RH_RF95_REG_28_FEI_MSB 0x28
+#define RH_RF95_REG_29_FEI_MID 0x29
+#define RH_RF95_REG_2A_FEI_LSB 0x2a
+#define RH_RF95_REG_2C_RSSI_WIDEBAND 0x2c
+#define RH_RF95_REG_31_DETECT_OPTIMIZ 0x31
+#define RH_RF95_REG_33_INVERT_IQ 0x33
+#define RH_RF95_REG_37_DETECTION_THRESHOLD 0x37
+#define RH_RF95_REG_39_SYNC_WORD 0x39
+
+#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_LOW_FREQUENCY_MODE 0x08
+#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_MAX_POWER 0x70
+#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_GAIN_G1 0x20
+#define RH_RF95_LNA_GAIN_G2 0x40
+#define RH_RF95_LNA_GAIN_G3 0x60
+#define RH_RF95_LNA_GAIN_G4 0x80
+#define RH_RF95_LNA_GAIN_G5 0xa0
+#define RH_RF95_LNA_GAIN_G6 0xc0
+#define RH_RF95_LNA_BOOST_LF 0x18
+#define RH_RF95_LNA_BOOST_LF_DEFAULT 0x00
+#define RH_RF95_LNA_BOOST_HF 0x03
+#define RH_RF95_LNA_BOOST_HF_DEFAULT 0x00
+#define RH_RF95_LNA_BOOST_HF_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 0xf0
+
+#define RH_RF95_BW_7_8KHZ 0x00
+#define RH_RF95_BW_10_4KHZ 0x10
+#define RH_RF95_BW_15_6KHZ 0x20
+#define RH_RF95_BW_20_8KHZ 0x30
+#define RH_RF95_BW_31_25KHZ 0x40
+#define RH_RF95_BW_41_7KHZ 0x50
+#define RH_RF95_BW_62_5KHZ 0x60
+#define RH_RF95_BW_125KHZ 0x70
+#define RH_RF95_BW_250KHZ 0x80
+#define RH_RF95_BW_500KHZ 0x90
+#define RH_RF95_CODING_RATE 0x0e
+#define RH_RF95_CODING_RATE_4_5 0x02
+#define RH_RF95_CODING_RATE_4_6 0x04
+#define RH_RF95_CODING_RATE_4_7 0x06
+#define RH_RF95_CODING_RATE_4_8 0x08
+#define RH_RF95_IMPLICIT_HEADER_MODE_ON 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_PAYLOAD_CRC_ON 0x04
+#define RH_RF95_SYM_TIMEOUT_MSB 0x03
+
+// RH_RF95_REG_4B_TCXO 0x4b
+#define RH_RF95_TCXO_TCXO_INPUT_ON 0x10
+
+// 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/79 and HopeRF RF95/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
+/// and http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf
+/// FSK/GFSK/OOK modes are not (yet) supported.
+///
+/// Works with
+/// - the excellent MiniWirelessLoRa from Anarduino http://www.anarduino.com/miniwireless
+/// - The excellent Modtronix inAir4 http://modtronix.com/inair4.html
+/// and inAir9 modules http://modtronix.com/inair9.html.
+/// - the excellent Rocket Scream Mini Ultra Pro with the RFM95W
+/// http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/
+/// - Lora1276 module from NiceRF http://www.nicerf.com/product_view.aspx?id=99
+/// - Adafruit Feather M0 with RFM95
+///
+/// \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), Semtech SX1276/77/78/79 and compatible radio modules in LoRa mode.
+///
+/// The Hope-RF (http://www.hoperf.com) RFM95/96/97/98(W) and Semtech SX1276/77/78/79 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 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.
+/// Also tested with Teensy3.1, Modtronix inAir4 and Arduino 1.6.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 and Semtech SX1276/77/78/79 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 (CS 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).
+///
+/// You can connect a Modtronix inAir4 or inAir9 directly to a 3.3V part such as a Teensy 3.1 like
+/// this (tested).
+/// \code
+/// Teensy inAir4 inAir9
+/// GND----------GND (ground in)
+/// 3V3----------3.3V (3.3V in)
+/// interrupt 0 pin D2-----------D00 (interrupt request out)
+/// SS pin D10----------CS (CS chip select in)
+/// SCK pin D13----------CK (SPI clock in)
+/// MOSI pin D11----------SI (SPI Data in)
+/// MISO pin D12----------SO (SPI Data out)
+/// \endcode
+/// With these connections, you can then use the default constructor RH_RF95().
+/// you must also set the transmitter power with useRFO:
+/// driver.setTxPower(13, true);
+///
+/// Note that if you are using Modtronix inAir4 or inAir9,or any other module which uses the
+/// transmitter RFO pins and not the PA_BOOST pins
+/// that you must configure the power transmitter power for -1 to 14 dBm and with useRFO true.
+/// Failure to do that will result in extremely low transmit powers.
+///
+/// If you have an Arduino M0 Pro from arduino.org,
+/// you should note that you cannot use Pin 2 for the interrupt line
+/// (Pin 2 is for the NMI only). The same comments apply to Pin 4 on Arduino Zero from arduino.cc.
+/// Instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this:
+/// \code
+/// // Slave Select is pin 10, interrupt is Pin 3
+/// RH_RF95 driver(10, 3);
+/// \endcode
+///
+/// If you have a Rocket Scream Mini Ultra Pro with the RFM95W:
+/// - Ensure you have Arduino SAMD board support 1.6.5 or later in Arduino IDE 1.6.8 or later.
+/// - The radio SS is hardwired to pin D5 and the DIO0 interrupt to pin D2,
+/// so you need to initialise the radio like this:
+/// \code
+/// RH_RF95 driver(5, 2);
+/// \endcode
+/// - The name of the serial port on that board is 'SerialUSB', not 'Serial', so this may be helpful at the top of our
+/// sample sketches:
+/// \code
+/// #define Serial SerialUSB
+/// \endcode
+/// - You also need this in setup before radio initialisation
+/// \code
+/// // Ensure serial flash is not interfering with radio communication on SPI bus
+/// pinMode(4, OUTPUT);
+/// digitalWrite(4, HIGH);
+/// \endcode
+/// - and if you have a 915MHz part, you need this after driver/manager intitalisation:
+/// \code
+/// rf95.setFrequency(915.0);
+/// \endcode
+/// which adds up to modifying sample sketches something like:
+/// \code
+/// #include <SPI.h>
+/// #include <RH_RF95.h>
+/// RH_RF95 rf95(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W
+/// #define Serial SerialUSB
+///
+/// void setup()
+/// {
+/// // Ensure serial flash is not interfering with radio communication on SPI bus
+/// pinMode(4, OUTPUT);
+/// digitalWrite(4, HIGH);
+///
+/// Serial.begin(9600);
+/// while (!Serial) ; // Wait for serial port to be available
+/// if (!rf95.init())
+/// Serial.println("init failed");
+/// rf95.setFrequency(915.0);
+/// }
+/// ...
+/// \endcode
+///
+/// For Adafruit Feather M0 with RFM95, construct the driver like this:
+/// \code
+/// RH_RF95 rf95(8, 3);
+/// \endcode
+///
+/// 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.
+///
+/// Caution: the performance of this radio, especially with narrow bandwidths is strongly dependent on the
+/// accuracy and stability of the chip clock. HopeRF and Semtech do not appear to
+/// recommend bandwidths of less than 62.5 kHz
+/// unless you have the optional Temperature Compensated Crystal Oscillator (TCXO) installed and
+/// enabled on your radio module. See the refernece manual for more data.
+/// Also https://lowpowerlab.com/forum/rf-range-antennas-rfm69-library/lora-library-experiences-range/15/
+/// and http://www.semtech.com/images/datasheet/an120014-xo-guidance-lora-modulation.pdf
+///
+/// \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 (for modules that use PA_BOOST)
+/// -1 to +14 (for modules that use RFO transmitter pin)
+/// The default is 13. Eg:
+/// \code
+/// driver.setTxPower(10); // use PA_BOOST transmitter pin
+/// driver.setTxPower(10, true); // use PA_RFO pin transmitter pin
+/// \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
+///
+/// We have also measured the actual power output from a Modtronix inAir4 http://modtronix.com/inair4.html
+/// connected to a Teensy 3.1:
+/// Teensy 3.1 this is a 3.3V part, connected directly to:
+/// Modtronix inAir4 with SMA antenna connector, connected as above:
+/// 10cm SMA-SMA cable
+/// - 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 0
+/// 1 2
+/// 3 4
+/// 5 7
+/// 7 10
+/// 9 13
+/// 11 14.2
+/// 13 15
+/// 14 16
+/// \endcode
+/// (Caution: we dont claim laboratory accuracy for these power 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 RHGenericDriver
+{
+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).
+ /// Caution: if you are using slow packet rates and long packets with RHReliableDatagram or subclasses
+ /// you may need to change the RHReliableDatagram timeout for reliable operations.
+ /// Caution: for some slow rates nad with ReliableDatagrams youi may need to increase the reply timeout
+ /// with manager.setTimeout() to
+ /// deal with the long transmission times.
+ 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 Arduino Zero from arduino.cc, any digital pin other than 4.
+ /// On Arduino M0 Pro from arduino.org, any digital pin other than 2.
+ /// 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.
+ /// \param[in] ssNum the swspi slave select number to use
+ RH_RF95(swspi& spi, int ssNum);
+
+ /// 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 optionally waits for Channel Activity Detection (CAD)
+ /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD().
+ /// 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
+ /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting.
+ /// \return true if the message length was valid and it was correctly queued for transmit. Return false
+ /// if CAD was requested and the CAD timeout timed out before clear channel was detected.
+ 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, and configures the transmitter pin.
+ /// Be a good neighbour and set the lowest power level you need.
+ /// Some SX1276/77/78/79 and compatible modules (such as RFM95/96/97/98)
+ /// use the PA_BOOST transmitter pin for high power output (and optionally the PA_DAC)
+ /// while some (such as the Modtronix inAir4 and inAir9)
+ /// use the RFO transmitter pin for lower power but higher efficiency.
+ /// You must set the appropriate power level and useRFO argument for your module.
+ /// Check with your module manufacturer which transmtter pin is used on your module
+ /// to ensure you are setting useRFO correctly.
+ /// Failure to do so will result in very low
+ /// transmitter power output.
+ /// Caution: legal power limits may apply in certain countries.
+ /// After init(), the power will be set to 13dBm, with useRFO false (ie PA_BOOST enabled).
+ /// \param[in] power Transmitter power level in dBm. For RFM95/96/97/98 LORA with useRFO false,
+ /// valid values are from +5 to +23.
+ /// For Modtronix inAir4 and inAir9 with useRFO true (ie RFO pins in use),
+ /// valid values are from -1 to 14.
+ /// \param[in] useRFO If true, enables the use of the RFO transmitter pins instead of
+ /// the PA_BOOST pin (false). Choose the correct setting for your module.
+ void setTxPower(int8_t power, bool useRFO = false);
+
+ /// 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();
+
+ // Bent G Christensen (bentor@gmail.com), 08/15/2016
+ /// Use the radio's Channel Activity Detect (CAD) function to detect channel activity.
+ /// Sets the RF95 radio into CAD mode and waits until CAD detection is complete.
+ /// To be used in a listen-before-talk mechanism (Collision Avoidance)
+ /// with a reasonable time backoff algorithm.
+ /// This is called automatically by waitCAD().
+ /// \return true if channel is in use.
+ virtual bool isChannelActive();
+
+ /// Enable TCXO mode
+ /// Call this immediately after init(), to force your radio to use an external
+ /// frequency source, such as a Temperature Compensated Crystal Oscillator (TCXO).
+ /// See the comments in the main documentation about the sensitivity of this radio to
+ /// clock frequency especially when using narrow bandwidths.
+ /// Leaves the module in sleep mode.
+ /// Caution, this function has not been tested by us.
+ void enableTCXO();
+
+ /// Returns the last measured frequency error.
+ /// The LoRa receiver estimates the frequency offset between the receiver centre frequency
+ /// and that of the received LoRa signal. This function returns the estimates offset (in Hz)
+ /// of the last received message. Caution: this measurement is not absolute, but is measured
+ /// relative to the local receiver's oscillator.
+ /// Apparent errors may be due to the transmitter, the receiver or both.
+ /// \return The estimated centre frequency offset in Hz of the last received message.
+ /// If the modem bandwidth selector in
+ /// register RH_RF95_REG_1D_MODEM_CONFIG1 is invalid, returns 0.
+ int frequencyError();
+
+ /// Returns the Signal-to-noise ratio (SNR) of the last received message, as measured
+ /// by the receiver.
+ /// \return SNR of the last received message in dB
+ int lastSNR();
+
+ /// This is a low level function to handle the interrupts for one instance of RH_RF95.
+ /// Must be called from user code in mbed because the swspi library mutex cannot be
+ /// executed in an ISR. It should be executed when the ISR pin is seen low.
+ void handleInterrupt();
+
+protected:
+ /// Examine the revceive buffer to determine whether the message is for this node
+ void validateRxBuf();
+
+ /// Clear our local receive buffer
+ void clearRxBuf();
+
+private:
+ /// SPI
+ swspi& _spi;
+ int _ssn;
+
+ /// 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;
+
+ // True if we are using the HF port (779.0 MHz and above)
+ bool _usingHFport;
+
+ // Last measured SNR, dB
+ int8_t _lastSNR;
+};
+
+/// @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 e69d086cb053 RadioHead.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RadioHead.h Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,2 @@
+// This is the address that indicates a broadcast
+#define RH_BROADCAST_ADDRESS 0xff
\ No newline at end of file