V148

Fork of RadioHead-148 by David Rimer

Revision:
0:ab4e012489ef
diff -r 000000000000 -r ab4e012489ef RHReliableDatagram.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RHReliableDatagram.h	Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,201 @@
+// 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
+