V148
Fork of RadioHead-148 by
RHReliableDatagram.h
- Committer:
- davidr99
- Date:
- 2015-10-15
- Revision:
- 0:ab4e012489ef
File content as of revision 0:ab4e012489ef:
// RHReliableDatagram.h // // Author: Mike McCauley (mikem@airspayce.com) // Copyright (C) 2011 Mike McCauley // $Id: RHReliableDatagram.h,v 1.16 2015/08/12 23:18:51 mikem Exp $ #ifndef RHReliableDatagram_h #define RHReliableDatagram_h #include <RHDatagram.h> // The acknowledgement bit in the FLAGS // The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved // for application layer use. #define RH_FLAGS_ACK 0x80 /// the default retry timeout in milliseconds #define RH_DEFAULT_TIMEOUT 200 /// The default number of retries #define RH_DEFAULT_RETRIES 3 ///////////////////////////////////////////////////////////////////// /// \class RHReliableDatagram RHReliableDatagram.h <RHReliableDatagram.h> /// \brief RHDatagram subclass for sending addressed, acknowledged, retransmitted datagrams. /// /// Manager class that extends RHDatagram to define addressed, reliable datagrams with acknowledgement and retransmission. /// Based on RHDatagram, adds flags and sequence numbers. RHReliableDatagram is reliable in the sense /// that messages are acknowledged by the recipient, and unacknowledged messages are retransmitted until acknowledged or the /// retries are exhausted. /// When addressed messages are sent (by sendtoWait()), it will wait for an ack, and retransmit /// after timeout until an ack is received or retries are exhausted. /// When addressed messages are collected by the application (by recvfromAck()), /// an acknowledgement is automatically sent to the sender. /// /// You can use RHReliableDatagram to send broadcast messages, with a TO address of RH_BROADCAST_ADDRESS, /// however broadcasts are not acknowledged or retransmitted and are therefore NOT actually reliable. /// /// The retransmit timeout is randomly varied between timeout and timeout*2 to prevent collisions on all /// retries when 2 nodes happen to start sending at the same time . /// /// Each new message sent by sendtoWait() has its ID incremented. /// /// An ack consists of a message with: /// - TO set to the from address of the original message /// - FROM set to this node address /// - ID set to the ID of the original message /// - FLAGS with the RH_FLAGS_ACK bit set /// - 1 octet of payload containing ASCII '!' (since some drivers cannot handle 0 length payloads) /// /// \par Media Access Strategy /// /// RHReliableDatagram and the underlying drivers always transmit as soon as /// sendtoWait() is called. RHReliableDatagram waits for an acknowledgement, /// and if one is not received after a timeout period the message is /// transmitted again. If no acknowledgement is received after several /// retries, the transmissions is deemed to have failed. /// No contention for media is detected. /// This will be recognised as "pure ALOHA". /// The addition of Clear Channel Assessment (CCA) is desirable and planned. /// /// There is no message queuing or threading in RHReliableDatagram. /// sendtoWait() waits until an acknowledgement is received, retransmitting /// up to (by default) 3 retries time with a default 200ms timeout. /// During this transmit-acknowledge phase, any received message (other than the expected /// acknowledgement) will be ignored. Your sketch will be unresponsive to new messages /// until an acknowledgement is received or the retries are exhausted. /// Central server-type sketches should be very cautious about their /// retransmit strategy and configuration lest they hang for a long time /// trying to reply to clients that are unreachable. /// /// Caution: if you have a radio network with a mixture of slow and fast /// processors and ReliableDatagrams, you may be affected by race conditions /// where the fast processor acknowledges a message before the sender is ready /// to process the acknowledgement. Best practice is to use the same processors (and /// radios) throughout your network. /// class RHReliableDatagram : public RHDatagram { public: /// Constructor. /// \param[in] driver The RadioHead driver to use to transport messages. /// \param[in] thisAddress The address to assign to this node. Defaults to 0 RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0); /// Sets the minimum retransmit timeout. If sendtoWait is waiting for an ack /// longer than this time (in milliseconds), /// it will retransmit the message. Defaults to 200ms. The timeout is measured from the end of /// transmission of the message. It must be at least longer than the the transmit /// time of the acknowledgement (preamble+6 octets) plus the latency/poll time of the receiver. /// For fast modulation schemes you can considerably shorten this time. /// The actual timeout is randomly varied between timeout and timeout*2. /// \param[in] timeout The new timeout period in milliseconds void setTimeout(uint16_t timeout); /// Sets the maximum number of retries. Defaults to 3 at construction time. /// If set to 0, each message will only ever be sent once. /// sendtoWait will give up and return false if there is no ack received after all transmissions time out /// and the retries count is exhausted. /// param[in] retries The maximum number a retries. void setRetries(uint8_t retries); /// Returns the currently configured maximum retries count. /// Can be changed with setRetries(). /// \return The currently configured maximum number of retries. uint8_t retries(); /// Send the message (with retries) and waits for an ack. Returns true if an acknowledgement is received. /// Synchronous: any message other than the desired ACK received while waiting is discarded. /// Blocks until an ACK is received or all retries are exhausted (ie up to retries*timeout milliseconds). /// If the destination address is the broadcast address RH_BROADCAST_ADDRESS (255), the message will /// be sent as a broadcast, but receiving nodes do not acknowledge, and sendtoWait() returns true immediately /// without waiting for any acknowledgements. /// \param[in] address The address to send the message to. /// \param[in] buf Pointer to the binary message to send /// \param[in] len Number of octets to send /// \return true if the message was transmitted and an acknowledgement was received. bool sendtoWait(uint8_t* buf, uint8_t len, uint8_t address); /// If there is a valid message available for this node, send an acknowledgement to the SRC /// address (blocking until this is complete), then copy the message to buf and return true /// else return false. /// If a message is copied, *len is set to the length.. /// If from is not NULL, the SRC address is placed in *from. /// If to is not NULL, the DEST address is placed in *to. /// This is the preferred function for getting messages addressed to this node. /// If the message is not a broadcast, acknowledge to the sender before returning. /// 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 Available space in buf. Set to the actual number of octets copied. /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS /// (not just those addressed to this node). /// \return true if a valid message was copied to buf bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL); /// Similar to recvfromAck(), this will block until either a valid message available for this node /// or the timeout expires. Starts the receiver automatically. /// 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 Available space in buf. Set to the actual number of octets copied. /// \param[in] timeout Maximum time to wait in milliseconds /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS /// (not just those addressed to this node). /// \return true if a valid message was copied to buf bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL); /// Returns the number of retransmissions /// we have had to send since starting or since the last call to resetRetransmissions(). /// \return The number of retransmissions since initialisation. uint32_t retransmissions(); /// Resets the count of the number of retransmissions /// to 0. void resetRetransmissions(); protected: /// Send an ACK for the message id to the given from address /// Blocks until the ACK has been sent void acknowledge(uint8_t id, uint8_t from); /// Checks whether the message currently in the Rx buffer is a new message, not previously received /// based on the from address and the sequence. If it is new, it is acknowledged and returns true /// \return true if there is a message received and it is a new message bool haveNewMessage(); private: /// Count of retransmissions we have had to send uint32_t _retransmissions; /// The last sequence number to be used /// Defaults to 0 uint8_t _lastSequenceNumber; // Retransmit timeout (milliseconds) /// Defaults to 200 uint16_t _timeout; // Retries (0 means one try only) /// Defaults to 3 uint8_t _retries; /// Array of the last seen sequence number indexed by node address that sent it /// It is used for duplicate detection. Duplicated messages are re-acknowledged when received /// (this is generally due to lost ACKs, causing the sender to retransmit, even though we have already /// received that message) uint8_t _seenIds[256]; }; /// @example rf22_reliable_datagram_client.pde /// @example rf22_reliable_datagram_server.pde #endif