// RH_E32.h
//
// Definitions for E32-TTL-1W family serial radios:
//  http://www.cdebyte.com/en/product-view-news.aspx?id=108
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2017 Mike McCauley
// $Id: RH_E32.h,v 1.5 2020/04/09 23:40:34 mikem Exp $
// 

#ifndef RH_E32_h
#define RH_E32_h

#include <RHGenericDriver.h>

// The buffer in the E32 is 512 bytes, but we arbitrarily limit messages to a maximum of 58 octets
// We use some for headers, keeping fewer for RadioHead messages
// The E32 sends messages longer than 58 octets in several packets of 58 octets each, and since we dont have any
// framing or message level checksums there is a risk that long messages from 2 different sources
// could be incorrectly interpreted as a single message
#define RH_E32_MAX_PAYLOAD_LEN 58

// The length of the headers we add:
//  message length (including these headers)
//  to
//  from
//  id
//  flags
// The headers are inside the E32 payload
#define RH_E32_HEADER_LEN 5

// This is the maximum RadioHead user message length that can be supported by this module. Limited by
#define RH_E32_MAX_MESSAGE_LEN (RH_E32_MAX_PAYLOAD_LEN-RH_E32_HEADER_LEN)

// Commands to alter module behaviour
#define RH_E32_COMMAND_WRITE_PARAMS_SAVE         0xC0
#define RH_E32_COMMAND_READ_PARAMS               0xC1
#define RH_E32_COMMAND_WRITE_PARAMS_NOSAVE       0xC2
#define RH_E32_COMMAND_READ_VERSION              0xC3
#define RH_E32_COMMAND_RESET                     0xC4

// Various flags and masks for param bytes
#define RH_E32_PARAM_SPED_UART_MODE_MASK         0xC0
#define RH_E32_PARAM_SPED_UART_MODE_8N1          0x00
#define RH_E32_PARAM_SPED_UART_MODE_8O1          0x40
#define RH_E32_PARAM_SPED_UART_MODE_8E1          0x80

#define RH_E32_PARAM_SPED_UART_BAUD_MASK         0x38
#define RH_E32_PARAM_SPED_UART_BAUD_1200         0x00
#define RH_E32_PARAM_SPED_UART_BAUD_2400         0x08
#define RH_E32_PARAM_SPED_UART_BAUD_4800         0x10
#define RH_E32_PARAM_SPED_UART_BAUD_9600         0x18
#define RH_E32_PARAM_SPED_UART_BAUD_19200        0x20
#define RH_E32_PARAM_SPED_UART_BAUD_38400        0x28
#define RH_E32_PARAM_SPED_UART_BAUD_57600        0x30
#define RH_E32_PARAM_SPED_UART_BAUD_115200       0x38

#define RH_E32_PARAM_SPED_DATARATE_MASK          0x07
#define RH_E32_PARAM_SPED_DATARATE_1KBPS         0x00
#define RH_E32_PARAM_SPED_DATARATE_2KBPS         0x01
#define RH_E32_PARAM_SPED_DATARATE_5KBPS         0x02
#define RH_E32_PARAM_SPED_DATARATE_8KBPS         0x03
#define RH_E32_PARAM_SPED_DATARATE_10KBPS        0x04
#define RH_E32_PARAM_SPED_DATARATE_15KBPS        0x05
#define RH_E32_PARAM_SPED_DATARATE_20KBPS        0x06
#define RH_E32_PARAM_SPED_DATARATE_25KBPS        0x07

#define RH_E32_PARAM_OPTION_FIXED_MASK           0x80

#define RH_E32_PARAM_OPTION_IODRIVE_MASK         0x40

#define RH_E32_PARAM_OPTION_WAKEUP_TIME_MASK     0x38
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_250MS    0x00
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_500MS    0x08
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_750MS    0x10
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1000MS   0x18
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1250MS   0x20
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1500MS   0x28
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1750MS   0x30
#define RH_E32_PARAM_OPTION_WAKEUP_TIME_2000MS   0x38

#define RH_E32_PARAM_OPTION_FEC_MASK             0x04

#define RH_E32_PARAM_OPTION_POWER_MASK           0x03
#define RH_E32_PARAM_OPTION_POWER_30DBM          0x00
#define RH_E32_PARAM_OPTION_POWER_27DBM          0x01
#define RH_E32_PARAM_OPTION_POWER_24DBM          0x02
#define RH_E32_PARAM_OPTION_POWER_21DBM          0x03

/////////////////////////////////////////////////////////////////////
/// \class RH_E32 RH_E32.h <RH_E32.h>
/// \brief Driver to send and receive unaddressed, unreliable datagrams via a EBYTE E32-TTL-1W
/// and similar serial radio transceiver.
///
/// Works with
///  E32-TTL-1W
///
/// Note: it should also be possible to use the E32-TTL-1W with the RadioHead RH_Serial module,
/// which will also you to send longer packets, but will require you to use the EBYTE Wireless Module Setting program
/// to configure the radio first. In this arrangement the E32 would act as a transparent serial connection.
/// This has not been tested by us.
///
/// \par Overview
///
/// This class provides basic functions for sending and receiving unaddressed, 
/// unreliable datagrams of arbitrary length to 53 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 EBYTE
/// RFM95/96/97/98(W), Semtech SX1276/77/78/7E32-TTL-1W9 and compatible radio modules. These modules implement
/// long range LORA transcivers with a transparent serial interface. With 1W power output the manufacturer
/// claims up to 6km range.
///
/// This Driver provides functions for sending and receiving messages of up
/// to 53 octets on any frequency supported by the radio, in a range of
/// data rates and power outputs. Frequency can be set with
/// 1MHz precision to any frequency from 410 to 441MHz.
///
/// You can use either a hardware or software serial connection.
///
/// Tested with Arduino Uno and software serial.
///
/// \par Packet Format
///
/// All messages sent and received by this Driver conform to this packet format:
///
/// - 5 octets HEADER: (LENGTH,  TO, FROM, ID, FLAGS)
/// - 0 to 53 octets DATA
///
/// \par Connecting E32-TTL-1W to Arduino
///
/// We tested with Arduino Uno. We used SoftwareSerial on pins 6 and 7) to connect to the E32 module, so
/// we could continue to use the only hardware serial port for debugging
/// \code
///                 Arduino      E32
///                 GND----------GND   (ground in)
///                 5V-----------VCC   (5V in)
///             pin D4-----------M0    (mode control pin input to radio)
///             pin D5-----------M1    (mode control pin input to radio)
///             pin D6-----------RXD   (serial data input from Arduino to radio)
///             pin D7-----------TXD   (serial data output from radio to Arduino)
///             pin D8-----------AUX   (Aux pin output from radio to Arduino)
/// \endcode
/// With this connection, you can initialise the serial port and RH_E32 like this:
/// \code
/// SoftwareSerial mySerial(7, 6);
/// RH_E32  driver(&mySerial, 4, 5, 8);
/// \endcode
///
/// For Adafruit M0 Feather:
/// \code
///                 Feather      E32
///                 GND----------GND   (ground in)
///                 3V-----------VCC   (3.3V in)
///             pin D5-----------M0    (mode control pin input to radio)
///             pin D6-----------M1    (mode control pin input to radio)
///             pin D1/Tx--------RXD   (serial data input from M0 to radio)
///             pin D0/Rx--------TXD   (serial data output from radio to M0)
///             pin D9-----------AUX   (Aux pin output from radio to M0)
/// \endcode
/// With this connection, you can initialise serial port 1 and RH_E32 like this:
/// \code
/// RH_E32  driver(&Serial1, 5, 6, 9);
/// \endcode
/// Other connection schems are possible provided the approporiate constructors are used for SoftwareSerial and RH_E32
///
/// \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_E32 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 Performance
///
/// This radio supports a range of different data rates and powers.
/// The lowest speeds are the most reliable, however you should note that at 1kbps and with an 13 octet payload,
/// the transmission time for one packet approaches 5 seconds. Therefore you should be cautious about trying to
/// send too many or too long messages per unit of time, lest you monopolise the airwaves.
/// Be a good neighbour and use the lowest power and fastest speed that you can.
///
/// Forward Error Correction (FEC) is always enabled in these radios by RH_E32.
///
/// \par Range
///
/// When running with a power output of 1W and at the slowest speed of 1kbps, this module has an impressive range.
/// We tested with:
///  E32-TTL-1W (1 W power, 1kbps data rate)
///  Single wire antenna with a small meta ground plane at about 1m above ground level
///  Arduino Uno
///  RadioHead RH_E32 module with e32_client and e32_server sketches
///  Packet length 13 octets (total payload 18 octets)
///  (and yes, we have an appropriate radio license for that power output)
///
/// We were able to get reliable reception over 7km (6 km over ocean and 1 km through low rise residential area)
///
/// You can expect less range with lower power outputs and faster speeds.
/// You can expect less range in highrise cities.
/// You can expect more range with directional antennas.
/// You can expect more range with shorter messages.
/// 
/// \par Transmitter Power
///   TBA
///
/// Caution: the maximum power output of this radio (1W = 30dbM) is almost certainly more than the
/// permitted power level for unlicensed users in the ISM bands in most countries. Be sure you comply with your local
/// regulations. Be a good neighbour and use the lowest power and fastest speed that you can.
///
class Stream;
class RH_E32 : public RHGenericDriver
{
 public:

    /// \brief Values to be passed to setDataRate() to control the on-air data rate
    ///
    /// This is NOT to be used to control the baud rate of the serial connection to the radio
    typedef enum
    {
      DataRate1kbps  = RH_E32_PARAM_SPED_DATARATE_1KBPS,
      DataRate2kbps  = RH_E32_PARAM_SPED_DATARATE_2KBPS,
      DataRate5kbps  = RH_E32_PARAM_SPED_DATARATE_5KBPS,
      DataRate8kbps  = RH_E32_PARAM_SPED_DATARATE_8KBPS,
      DataRate10kbps = RH_E32_PARAM_SPED_DATARATE_10KBPS,
      DataRate15kbps = RH_E32_PARAM_SPED_DATARATE_15KBPS,
      DataRate20kbps = RH_E32_PARAM_SPED_DATARATE_20KBPS,
      DataRate25kbps = RH_E32_PARAM_SPED_DATARATE_25KBPS
    } DataRate;
    
    /// \brief Values to be passed to setPower() to control the transmitter power
    ///
    typedef enum
    {
      Power30dBm = RH_E32_PARAM_OPTION_POWER_30DBM,
      Power27dBm = RH_E32_PARAM_OPTION_POWER_27DBM,
      Power24dBm = RH_E32_PARAM_OPTION_POWER_24DBM,
      Power21dBm = RH_E32_PARAM_OPTION_POWER_21DBM,
    } PowerLevel;

    /// \brief Values to be passed to setBaudRate() to control the radio serial connection baud rate
    ///
    /// This is NOT to be used to control the on-air data rate the radio transmits and receives at
    typedef enum
    {
      BaudRate1200   = RH_E32_PARAM_SPED_UART_BAUD_1200,
      BaudRate2400   = RH_E32_PARAM_SPED_UART_BAUD_2400,
      BaudRate4800   = RH_E32_PARAM_SPED_UART_BAUD_4800,
      BaudRate9600   = RH_E32_PARAM_SPED_UART_BAUD_9600,
      BaudRate19200  = RH_E32_PARAM_SPED_UART_BAUD_19200,
      BaudRate38400  = RH_E32_PARAM_SPED_UART_BAUD_38400,
      BaudRate57600  = RH_E32_PARAM_SPED_UART_BAUD_57600,
      BaudRate115200 = RH_E32_PARAM_SPED_UART_BAUD_115200,
    } BaudRate;

    /// \brief Values to be passed to setBaudRate() to control the parity of the serial connection to the radio
    typedef enum
    {
      Parity8N1 = RH_E32_PARAM_SPED_UART_MODE_8N1,
      Parity8O1 = RH_E32_PARAM_SPED_UART_MODE_8O1,
      Parity8E1 = RH_E32_PARAM_SPED_UART_MODE_8E1,
    } Parity;
 
    /// Contructor. You can have multiple instances, but each instance must have its own
    /// serial connection, M0 M1 and AUX connections. Initialises the mode of the referenced pins
    /// Does NOT set the baud rate of the serial connection to the radio.
    /// \param[in] s Reference to the SoftwareSerial or HardwareSerial port used to connect to the radio
    /// \param[in] m0_pin Pin number of the Arduino pin that connects to the radio M0 input
    /// \param[in] m1_pin Pin number of the Arduino pin that connects to the radio M1 input
    /// \param[in] aux_pin Pin number of the Arduino pin that connects to the radio AUX output
    RH_E32(Stream *s=&Serial, uint8_t m0_pin = 4, uint8_t m1_pin = 5, uint8_t aux_pin = 8);

    /// Initialise the Driver transport hardware and software.
    /// Make sure the Driver is properly, including setting the serial port baud rate and parity to that
    /// configured in the radio (typically 9600 baud, 8N1) before calling init().
    /// Sets the module to 443MHz, 21dBm power and 5kbps data rate (you can change these after initialisation with
    /// the various set* functions).
    /// This function may not return if the AUX pin is not connected.
    /// Initialisation failure can be caused by:
    /// Electrical connections to the radio incorrect or incomplete
    /// Radio configured to use a different baud rate to the one configured to the Ardiono serial port
    /// Incorrect radio module connected tot he serial port.
    /// Other serial communicaitons problems between the Arduino and the radio
    /// \return true if initialisation succeeded.
    bool init();

    /// Tests whether a new message is available
    /// from the Driver. 
    /// This can and should be called multiple times in a timeout loop. You should call this as frequently as possible
    /// whenever a message might be received
    /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv().
    bool available();
    
    /// 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 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. Return false
    /// if CAD was requested and the CAD timeout timed out before clear channel was detected.
    bool send(const uint8_t* data, uint8_t len);
    
    /// Returns the maximum message length 
    /// available in this Driver.
    /// \return The maximum legal message length
    uint8_t maxMessageLength();

    /// Waits for any currently transmitting packet to be completely sent
    /// Returns true if successful
    bool waitPacketSent();

    /// Sets the on-air data rate to be used by the transmitter and receiver
    /// \param[in] rate A valid data rate from the DataRate enum
    /// \return true if successful
    bool setDataRate(DataRate rate);
    
    /// Sets the transmitter power output
    /// \param[in] level A valid power setting from the Power enum
    /// \return true if successful
    bool setPower(PowerLevel level);
    
    /// Sets the radio serial port baud rate and parity (not the on-air data rate)
    /// Does not set the Aruino rate or parity: you wil nned to do this afterwards
    /// \param[in] rate A valid baud rate from the BaudRate enum
    /// \param[in] parity A valid parity from the PArity enum
    /// \return true if successful
    bool setBaudRate(BaudRate rate = BaudRate9600, Parity parity = Parity8N1);

    /// Sets the tarnsmitter and receiver frequency.
    /// \param[in] frequency Desired frequency in MHx from 410 to 441 MHz inclusive
    /// \return true if successful
    bool setFrequency(uint16_t frequency);
 
protected:

    /// \brief Defines values to be passed to setOperatinMode
    ///
    /// For internal driver user only
    typedef enum
    {
      ModeNormal = 0,    ///< Normal mode for sending and receiving messages
      ModeWakeUp,        ///< Adds a long preamble to transmission to allow destination receivers to wake up
      ModePowerSaving,   ///< Receiver sleeps until a message is received
      ModeSleep          ///< Use during parameter setting
    } OperatingMode;
    
    /// \brief Structure for reading and writing radio control parameters
    ///
    /// For internal driver user only
    typedef struct
    {
      uint8_t head;      ///< 0xc2 (no save) or 0xc0 (save)
      uint8_t addh;      ///< High address byte (not used by this driver)
      uint8_t addl;      ///< Low address byte (not used by this driver)
      uint8_t sped;      ///< Data and baud rate parameters
      uint8_t chan;      ///< Radio channel
      uint8_t option;    ///< Various control options
      
    } Parameters;

    /// Sets the operating mode of the radio.
    /// For internal use only
    void setOperatingMode(OperatingMode mode);

    /// Retrieves the version number for the radio and checks that it is valid
    /// \return true if the version could be retrieved and is radio model number is correct
    bool getVersion();

    /// Waits for the AUX pin to go high
    /// For internal use only
    void waitAuxHigh();

    /// Waits for the AUX pin to go low
    /// For internal use only
    void waitAuxLow();
    
    /// Issues a reset command to the radio
    /// WARNING: this seems to break reception. Why?
    /// \return true if successful
    bool reset();

    /// Read the radio configuration parameters into
    /// local memory
    /// \param[in] params Reference to a Parameter structure which will be filled if successful
    /// \return true if successful
    bool readParameters(Parameters& params);

    /// Write radio configuration parameters from local memory
    /// to the radio. You can choose whether the parameter will be saved across power down or not
    /// \param[in] params Reference to a Parameter structure containing the radio configuration parameters
    /// to be written to the radio.
    /// \param[in] save If true, the parameters will be saved across power down in the radio
    /// \return true if successful
    bool writeParameters(Parameters& params, bool save = false);

    /// Examine the receive buffer to determine whether the message is for this node
    /// For internal use only
    void validateRxBuf();

    /// Clear our local receive buffer
    /// For internal use only
    void clearRxBuf();

private:
    /// Serial stream (hardware or software serial)
    Stream*     _s;

    /// Pin number connected to M0
    uint8_t     _m0_pin;

    /// Pin number connected to M1
    uint8_t     _m1_pin;

    /// Pin number connected to AUX
    uint8_t     _aux_pin;
    
    /// Number of octets in the buffer
    uint8_t             _bufLen;
    
    /// The receiver/transmitter buffer
    uint8_t             _buf[RH_E32_MAX_PAYLOAD_LEN];

    /// True when there is a valid message in the buffer
    bool                _rxBufValid;

};

/// @example e32_client.pde
/// @example e32_server.pde

#endif

