// 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 #include // 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 /// \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 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 the number of octets available in buf. The number be reset 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.ino /// @example e32_server.ino #endif