// RH_TCP.h // Author: Mike McCauley (mikem@aierspayce.com) // Copyright (C) 2014 Mike McCauley // $Id: RH_TCP.h,v 1.4 2015/08/13 02:45:47 mikem Exp $ #ifndef RH_TCP_h #define RH_TCP_h #include #include ///////////////////////////////////////////////////////////////////// /// \class RH_TCP RH_TCP.h /// \brief Driver to send and receive unaddressed, unreliable datagrams via sockets on a Linux simulator /// /// \par Overview /// /// This class is intended to support the testing of RadioHead manager classes and simulated sketches /// on a Linux host. /// RH_TCP class sends messages to and from other simulator sketches via sockets to a 'Luminiferous Ether' /// simulator server (provided). /// Multiple instances of simulated clients and servers can run on a single Linux server, /// passing messages to each other via the etherSimulator.pl server. /// /// Simple RadioHead sketches can be compiled and run on Linux using a build script and some support files. /// /// \par Running simulated sketches /// /// \code /// cd whatever/RadioHead /// # build the client for Linux: /// tools/simBuild examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde /// # build the server for Linux: /// tools/simBuild examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde /// # in one window, run the simulator server: /// tools/etherSimulator.pl /// # in another window, run the server /// ./simulator_reliable_datagram_server /// # in another window, run the client: /// ./simulator_reliable_datagram_client /// # see output: /// Sending to simulator_reliable_datagram_server /// got reply from : 0x02: And hello back to you /// Sending to simulator_reliable_datagram_server /// got reply from : 0x02: And hello back to you /// Sending to simulator_reliable_datagram_server /// got reply from : 0x02: And hello back to you /// ... /// \endcode /// /// You can change the listen port and the simulated baud rate with /// command line arguments passed to etherSimulator.pl /// /// \par Implementation /// /// etherServer.pl is a conventional server written in Perl. /// listens on a TCP socket (defaults to port 4000) for connections from sketch simulators /// using RH_TCP as theur driver. /// The simulated sketches send messages out to the 'ether' over the TCP connection to the etherServer. /// etherServer manages the delivery of each message to any other RH_TCP sketches that are running. /// /// \par Prerequisites /// /// g++ compiler installed and in your $PATH /// Perl /// Perl POE library /// class RH_TCP : public RHGenericDriver { public: /// Constructor /// \param[in] server Name and optionally the port number of the ether simulator server to contact. /// Format is "name[:port]", where name can be any valid host name or address (IPV4 or IPV6). /// The trailing :port is optional, and port can be any valid /// port name or port number. RH_TCP(const char* server = "localhost:4000"); /// Initialise the Driver transport hardware and software. /// Make sure the Driver is properly configured before calling init(). /// \return true if initialisation succeeded. virtual bool init(); /// Tests whether a new message is available /// from the Driver. /// On most drivers, this will also put the Driver into RHModeRx mode until /// a message is actually received by the transport, when it will be returned to RHModeIdle. /// This can be called multiple times in a timeout loop /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() virtual bool available(); /// Wait until a new message is available from the driver. /// Blocks until a complete message is received as reported by available() virtual void waitAvailable(); /// Wait until a new message is available from the driver /// or the timeout expires /// Blocks until a complete message is received as reported by available() /// \param[in] timeout The maximum time to wait in milliseconds /// \return true if a message is available as reported by available() virtual bool waitAvailableTimeout(uint16_t timeout); /// Turns the receiver on if it not already on. /// If there is a valid message available, copy it to buf and return true /// else return false. /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). /// You should be sure to call this function frequently enough to not miss any messages /// It is recommended that you call it in your main loop. /// \param[in] buf Location to copy the received message /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. /// \return true if a valid message was copied to buf virtual bool recv(uint8_t* buf, uint8_t* len); /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). /// Then loads a message into the transmitter and starts the transmitter. Note that a message length /// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will /// return false and will not send the message. /// \param[in] data Array of data to be sent /// \param[in] len Number of bytes of data to send (> 0) /// \return true if the message length was valid and it was correctly queued for transmit virtual bool send(const uint8_t* data, uint8_t len); /// Returns the maximum message length /// available in this Driver. /// \return The maximum legal message length virtual uint8_t maxMessageLength(); /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. /// In promiscuous mode, all messages will be accepted regardless of the TO header. /// In a conventional multinode system, all nodes will have a unique address /// (which you could store in EEPROM). /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, /// allowing the possibilty of address spoofing). /// \param[in] address The address of this node. void setThisAddress(uint8_t address); protected: private: /// Connect to the address and port specified by the server constructor argument. /// Prepares the socket for use. bool connectToServer(); /// Check for new messages from the ether simulator server void checkForEvents(); /// Clear the receive buffer void clearRxBuf(); /// Sends thisAddress to the ether simulator server /// in a RHTcpThisAddress message. /// \param[in] thisAddress The node address of this node /// \return true if successful bool sendThisAddress(uint8_t thisAddress); /// Sends a message to the ether simulator server for delivery to /// other nodes /// \param[in] data Array of data to be sent /// \param[in] len Number of bytes of data to send (> 0) /// \return true if successful bool sendPacket(const uint8_t* data, uint8_t len); /// Address and port of the server to which messages are sent /// and received using the protocol RHTcpPRotocol const char* _server; /// The TCP socket used to communicate with the message server int _socket; /// Buffer to receive RHTcpProtocol messages uint8_t _rxBuf[RH_TCP_MAX_PAYLOAD_LEN + 5]; uint16_t _rxBufLen; bool _rxBufValid; /// Check whether the latest received message is complete and uncorrupted void validateRxBuf(); // Used in the interrupt handlers /// Buf is filled but not validated volatile bool _rxBufFull; }; /// @example simulator_reliable_datagram_client.pde /// @example simulator_reliable_datagram_server.pde #endif