|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- // RH_Serial.h
- //
- // Copyright (C) 2014 Mike McCauley
- // $Id: RH_Serial.h,v 1.11 2016/04/04 01:40:12 mikem Exp $
-
- // Works with any serial port. Tested with Arduino Mega connected to Serial1
- // Also works with 3DR Radio V1.3 Telemetry kit (serial at 57600baud)
-
- #ifndef RH_Serial_h
- #define RH_Serial_h
-
- #include <RHGenericDriver.h>
-
- // Special characters
- #define STX 0x02
- #define ETX 0x03
- #define DLE 0x10
- #define SYN 0x16
-
- // Maximum message length (including the headers) we are willing to support
- #define RH_SERIAL_MAX_PAYLOAD_LEN 64
-
- // The length of the headers we add.
- // The headers are inside the payload and are therefore protected by the FCS
- #define RH_SERIAL_HEADER_LEN 4
-
- // This is the maximum message length that can be supported by this library.
- // It is an arbitrary limit.
- // Can be pre-defined to a smaller size (to save SRAM) prior to including this header
- // Here we allow for 4 bytes of address and header and payload to be included in the 64 byte encryption limit.
- // the one byte payload length is not encrpyted
- #ifndef RH_SERIAL_MAX_MESSAGE_LEN
- #define RH_SERIAL_MAX_MESSAGE_LEN (RH_SERIAL_MAX_PAYLOAD_LEN - RH_SERIAL_HEADER_LEN)
- #endif
-
- #if (RH_PLATFORM == RH_PLATFORM_STM32F2)
- #define HardwareSerial USARTSerial
- #endif
-
- class HardwareSerial;
-
- /////////////////////////////////////////////////////////////////////
- /// \class RH_Serial RH_Serial.h <RH_Serial.h>
- /// \brief Driver to send and receive unaddressed, unreliable datagrams via a serial connection
- ///
- /// This class sends and received packetized messages over a serial connection.
- /// It can be used for point-to-point or multidrop, RS232, RS488 or other serial connections as
- /// supported by your controller hardware.
- /// It can also be used to communicate via radios with serial interfaces such as:
- /// - APC220 Radio Data Module http://www.dfrobot.com/image/data/TEL0005/APC220_Datasheet.pdf
- /// http://www.dfrobot.com/image/data/TEL0005/APC220_Datasheet.pdf
- /// - 3DR Telemetry Radio https://store.3drobotics.com/products/3dr-radio
- /// - HopeRF HM-TR module http://www.hoperf.com/upload/rf_app/HM-TRS.pdf
- /// - Others
- ///
- /// Compiles and runs on Linux, OSX and all the microprocessers and MCUs suported by
- /// radiohead. On Linux and OSX, a RadioHead specific version of HardwareSerial (in RHutil/HardwareSerial.*)
- /// encapsulates access to any serial port (or suported USB-serial converter)
- ///
- /// The packetised messages include message encapsulation, headers, a message payload and a checksum.
- /// It therefore can support robust binary message passing with error-detection and retransmission
- /// when used with the appropriate manager. This allows reliable serial communicaitons even over very long
- /// lines where noise might otherwise affect reliablity of the communications.
- ///
- /// \par Packet Format
- ///
- /// All messages sent and received by this RH_Serial Driver conform to this packet format:
- /// \code
- /// DLE
- /// STX
- /// TO Header (1 octet)
- /// FROM Header (1 octet)
- /// ID Header (1 octet)
- /// FLAGS Header (1 octet)
- /// Message payload (0 to 60 octets)
- /// DLE
- /// ETX
- /// Frame Check Sequence FCS CCITT CRC-16 (2 octets)
- /// \endcode
- ///
- /// If any of octets from TO header through to the end of the payload are a DLE,
- /// then they are preceded by a DLE (ie DLE stuffing).
- /// The FCS covers everything from the TO header to the ETX inclusive, but not any stuffed DLEs
- ///
- /// \par Physical connection
- ///
- /// The physical connection to your serial port will depend on the type of platform you are on.
- ///
- /// For example, many arduinos only support a single Serial port on pins 0 and 1,
- /// which is shared with the USB host connections. On such Arduinos, it is not possible to use both
- /// RH_Serial on the Serial port as well as using the Serial port for debugand other printing or communications.
- ///
- /// On Arduino Mega and Due, there are 4 serial ports:
- /// - Serial: this is the serial port connected to the USB interface and the programming host.
- /// - Serial1: on pins 18 (Tx) and 19 (Rx)
- /// - Serial2: on pins 16 (Tx) and 17 (Rx)
- /// - Serial3: on pins 14 (Tx) and 15 (Rx)
- ///
- /// On Uno32, there are 2 serial ports:
- /// - SerialUSB: this is the port for the USB host connection.
- /// - Serial1: on pins 39 (Rx) and 40 (Tx)
- ///
- /// On Maple and Flymaple, there are 4 serial ports:
- /// - SerialUSB: this is the port for the USB host connection.
- /// - Serial1: on pins 7 (Tx) and 8 (Rx)
- /// - Serial2: on pins 0 (Rx) and 1 (Tx)
- /// - Serial3: on pins 29 (Tx) and 30 (Rx)
- ///
- /// On Linux and OSX there can be any number of serial ports.
- /// - On Linux, names like /dev/ttyUSB0 (for a FTDO USB-serial converter)
- /// - On OSX, names like /dev/tty.usbserial-A501YSWL (for a FTDO USB-serial converter)
- ///
- /// Note that it is necessary for you to select which Serial port your RF_Serial will use and pass it to the
- /// contructor. On Linux you must pass an instance of HardwareSerial.
- ///
- /// \par Testing
- ///
- /// You can test this class and the RHReliableDatagram manager
- /// on Unix and OSX with back-to-back connected FTDI USB-serial adapters.
- /// Back-to-back means the TX of one is connected to the RX of the other and vice-versa.
- /// You should also join the ground pins.
- ///
- /// Assume the 2 USB-serial adapters are connected by USB
- /// and have been assigned device names:
- /// /dev/ttyUSB0 and /dev/ttyUSB1.
- /// Build the example RHReliableDatagram client and server programs:
- /// \code
- /// tools/simBuild examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde
- /// tools/simBuild examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde
- /// \endcode
- /// In one window run the server, specifying the device to use as an environment variable:
- /// \code
- /// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB1 ./serial_reliable_datagram_server
- /// \endcode
- /// And in another window run the client, specifying the other device to use as an environment variable:
- /// \code
- /// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB0 ./serial_reliable_datagram_client
- /// \endcode
- /// You should see the 2 programs passing messages to each other.
- ///
- class RH_Serial : public RHGenericDriver
- {
- public:
- /// Constructor
- /// \param[in] serial Reference to the HardwareSerial port which will be used by this instance.
- /// On Unix and OSX, this is an instance of RHutil/HardwareSerial. On
- /// Arduino and other, it is an instance of the built in HardwareSerial class.
- RH_Serial(HardwareSerial& serial);
-
- /// Return the HardwareSerial port in use by this instance
- /// \return The current HardwareSerial as a reference
- HardwareSerial& serial();
-
- /// 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
- /// This can be called multiple times in a timeout loop.
- /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
- virtual bool available();
-
- /// Wait until a new message is available from the driver.
- /// Blocks until a complete message is received as reported by available()
- virtual void waitAvailable();
-
- /// Wait until a new message is available from the driver or the timeout expires.
- /// Blocks until a complete message is received as reported by available() or the timeout expires.
- /// \param[in] timeout The maximum time to wait in milliseconds
- /// \return true if a message is available as reported by available(), false on timeout.
- virtual bool waitAvailableTimeout(uint16_t timeout);
-
- /// If there is a valid message available, copy it to buf and return true
- /// else return false.
- /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
- /// You should be sure to call this function frequently enough to not miss any messages
- /// It is recommended that you call it in your main loop.
- /// \param[in] buf Location to copy the received message
- /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
- /// \return true if a valid message was copied to buf
- virtual bool recv(uint8_t* buf, uint8_t* len);
-
- /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
- /// Then loads a message into the transmitter and starts the transmitter. Note that a message length
- /// of 0 is NOT permitted.
- /// \param[in] data Array of data to be sent
- /// \param[in] len Number of bytes of data to send (> 0)
- /// \return true if the message length was valid and it was correctly queued for transmit
- virtual bool send(const uint8_t* data, uint8_t len);
-
- /// Returns the maximum message length
- /// available in this Driver.
- /// \return The maximum legal message length
- virtual uint8_t maxMessageLength();
-
-
- protected:
- /// \brief Defines different receiver states in teh receiver state machine
- typedef enum
- {
- RxStateInitialising = 0, ///< Before init() is called
- RxStateIdle, ///< Waiting for an STX
- RxStateDLE, ///< Waiting for the DLE after STX
- RxStateData, ///< Receiving data
- RxStateEscape, ///< Got a DLE while receiving data.
- RxStateWaitFCS1, ///< Got DLE ETX, waiting for first FCS octet
- RxStateWaitFCS2 ///< Waiting for second FCS octet
- } RxState;
-
- /// HAndle a character received from the serial port. IMplements
- /// the receiver state machine
- void handleRx(uint8_t ch);
-
- /// Empties the Rx buffer
- void clearRxBuf();
-
- /// Adds a charater to the Rx buffer
- void appendRxBuf(uint8_t ch);
-
- /// Checks whether the Rx buffer contains valid data that is complete and uncorrupted
- /// Check the FCS, the TO address, and extracts the headers
- void validateRxBuf();
-
- /// Sends a single data octet to the serial port.
- /// Implements DLE stuffing and keeps track of the senders FCS
- void txData(uint8_t ch);
-
- /// Reference to the HardwareSerial port we will use
- HardwareSerial& _serial;
-
- /// The current state of the Rx state machine
- RxState _rxState;
-
- /// Progressive FCS calc (CCITT CRC-16 covering all received data (but not stuffed DLEs), plus trailing DLE, ETX)
- uint16_t _rxFcs;
-
- /// The received FCS at the end of the current message
- uint16_t _rxRecdFcs;
-
- /// The Rx buffer
- uint8_t _rxBuf[RH_SERIAL_MAX_PAYLOAD_LEN];
-
- /// Current length of data in the Rx buffer
- uint8_t _rxBufLen;
-
- /// True if the data in the Rx buffer is value and uncorrupted and complete message is available for collection
- bool _rxBufValid;
-
- /// FCS for transmitted data
- uint16_t _txFcs;
- };
-
- /// @example serial_reliable_datagram_client.pde
- /// @example serial_reliable_datagram_server.pde
- /// @example serial_gateway.pde
-
- #endif
|