RadioHead
RHReliableDatagram.h
1 // RHReliableDatagram.h
2 //
3 // Author: Mike McCauley (mikem@airspayce.com)
4 // Copyright (C) 2011 Mike McCauley
5 // $Id: RHReliableDatagram.h,v 1.19 2019/07/14 00:18:48 mikem Exp $
6 
7 #ifndef RHReliableDatagram_h
8 #define RHReliableDatagram_h
9 
10 #include <RHDatagram.h>
11 
12 /// The acknowledgement bit in the header FLAGS. This indicates if the payload is for an
13 /// ack for a successfully received message.
14 #define RH_FLAGS_ACK 0x80
15 /// The retry bit in the header FLAGS. This indicates that the payload is a retry for a
16 /// previously sent message.
17 #define RH_FLAGS_RETRY 0x40
18 
19 /// This macro enables enhanced message deduplication behavior. This currently defaults
20 /// to 0 (off), but this may change to default to 1 (on) in future releases. Consumers who
21 /// want to enable this behavior should override this macro in their code and set it to 1.
22 /// It is most useful where a transmitter periodically wakes up and starts to transmit
23 /// starting again from the first sequence number.
24 ///
25 /// Enhanced deduplication: Only messages containing the retry bit in the header
26 /// FLAGS will be evaluated for deduplication. This ensures that only messages that are
27 /// genuine retries will potentially be deduped. Note that this should not be enabled
28 /// if you will receive messages from devices using older versions of this library that
29 /// do not support the RETRY header. If you do, deduping of messages will be broken.
30 #define RH_ENABLE_EXPLICIT_RETRY_DEDUP 0
31 
32 /// the default retry timeout in milliseconds
33 #define RH_DEFAULT_TIMEOUT 200
34 
35 /// The default number of retries
36 #define RH_DEFAULT_RETRIES 3
37 
38 /////////////////////////////////////////////////////////////////////
39 /// \class RHReliableDatagram RHReliableDatagram.h <RHReliableDatagram.h>
40 /// \brief RHDatagram subclass for sending addressed, acknowledged, retransmitted datagrams.
41 ///
42 /// Manager class that extends RHDatagram to define addressed, reliable datagrams with acknowledgement and retransmission.
43 /// Based on RHDatagram, adds flags and sequence numbers. RHReliableDatagram is reliable in the sense
44 /// that messages are acknowledged by the recipient, and unacknowledged messages are retransmitted until acknowledged or the
45 /// retries are exhausted.
46 /// When addressed messages are sent (by sendtoWait()), it will wait for an ack, and retransmit
47 /// after timeout until an ack is received or retries are exhausted.
48 /// When addressed messages are collected by the application (by recvfromAck()),
49 /// an acknowledgement is automatically sent to the sender.
50 ///
51 /// You can use RHReliableDatagram to send broadcast messages, with a TO address of RH_BROADCAST_ADDRESS,
52 /// however broadcasts are not acknowledged or retransmitted and are therefore NOT actually reliable.
53 ///
54 /// The retransmit timeout is randomly varied between timeout and timeout*2 to prevent collisions on all
55 /// retries when 2 nodes happen to start sending at the same time .
56 ///
57 /// Each new message sent by sendtoWait() has its ID incremented.
58 ///
59 /// An ack consists of a message with:
60 /// - TO set to the from address of the original message
61 /// - FROM set to this node address
62 /// - ID set to the ID of the original message
63 /// - FLAGS with the RH_FLAGS_ACK bit set
64 /// - 1 octet of payload containing ASCII '!' (since some drivers cannot handle 0 length payloads)
65 ///
66 /// \par Media Access Strategy
67 ///
68 /// RHReliableDatagram and the underlying drivers always transmit as soon as
69 /// sendtoWait() is called. RHReliableDatagram waits for an acknowledgement,
70 /// and if one is not received after a timeout period the message is
71 /// transmitted again. If no acknowledgement is received after several
72 /// retries, the transmissions is deemed to have failed.
73 /// No contention for media is detected.
74 /// This will be recognised as "pure ALOHA".
75 /// The addition of Clear Channel Assessment (CCA) is desirable and planned.
76 ///
77 /// There is no message queuing or threading in RHReliableDatagram.
78 /// sendtoWait() waits until an acknowledgement is received, retransmitting
79 /// up to (by default) 3 retries time with a default 200ms timeout.
80 /// During this transmit-acknowledge phase, any received message (other than the expected
81 /// acknowledgement) will be ignored. Your sketch will be unresponsive to new messages
82 /// until an acknowledgement is received or the retries are exhausted.
83 /// Central server-type sketches should be very cautious about their
84 /// retransmit strategy and configuration lest they hang for a long time
85 /// trying to reply to clients that are unreachable.
86 ///
87 /// Caution: if you have a radio network with a mixture of slow and fast
88 /// processors and ReliableDatagrams, you may be affected by race conditions
89 /// where the fast processor acknowledges a message before the sender is ready
90 /// to process the acknowledgement. Best practice is to use the same processors (and
91 /// radios) throughout your network.
92 ///
94 {
95 public:
96  /// Constructor.
97  /// \param[in] driver The RadioHead driver to use to transport messages.
98  /// \param[in] thisAddress The address to assign to this node. Defaults to 0
99  RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0);
100 
101  /// Sets the minimum retransmit timeout. If sendtoWait is waiting for an ack
102  /// longer than this time (in milliseconds),
103  /// it will retransmit the message. Defaults to 200ms. The timeout is measured from the end of
104  /// transmission of the message. It must be at least longer than the the transmit
105  /// time of the acknowledgement (preamble+6 octets) plus the latency/poll time of the receiver.
106  /// For fast modulation schemes you can considerably shorten this time.
107  /// Caution: if you are using slow packet rates and long packets
108  /// you may need to change the timeout for reliable operations.
109  /// The actual timeout is randomly varied between timeout and timeout*2.
110  /// \param[in] timeout The new timeout period in milliseconds
111  void setTimeout(uint16_t timeout);
112 
113  /// Sets the maximum number of retries. Defaults to 3 at construction time.
114  /// If set to 0, each message will only ever be sent once.
115  /// sendtoWait will give up and return false if there is no ack received after all transmissions time out
116  /// and the retries count is exhausted.
117  /// param[in] retries The maximum number a retries.
118  void setRetries(uint8_t retries);
119 
120  /// Returns the currently configured maximum retries count.
121  /// Can be changed with setRetries().
122  /// \return The currently configured maximum number of retries.
123  uint8_t retries();
124 
125  /// Send the message (with retries) and waits for an ack. Returns true if an acknowledgement is received.
126  /// Synchronous: any message other than the desired ACK received while waiting is discarded.
127  /// Blocks until an ACK is received or all retries are exhausted (ie up to retries*timeout milliseconds).
128  /// If the destination address is the broadcast address RH_BROADCAST_ADDRESS (255), the message will
129  /// be sent as a broadcast, but receiving nodes do not acknowledge, and sendtoWait() returns true immediately
130  /// without waiting for any acknowledgements.
131  /// \param[in] address The address to send the message to.
132  /// \param[in] buf Pointer to the binary message to send
133  /// \param[in] len Number of octets to send
134  /// \return true if the message was transmitted and an acknowledgement was received.
135  bool sendtoWait(uint8_t* buf, uint8_t len, uint8_t address);
136 
137  /// If there is a valid message available for this node, send an acknowledgement to the SRC
138  /// address (blocking until this is complete), then copy the message to buf and return true
139  /// else return false.
140  /// If a message is copied, *len is set to the length.
141  /// If from is not NULL, the SRC address is placed in *from.
142  /// If to is not NULL, the DEST address is placed in *to.
143  /// This is the preferred function for getting messages addressed to this node.
144  /// If the message is not a broadcast, acknowledge to the sender before returning.
145  /// You should be sure to call this function frequently enough to not miss any messages.
146  /// It is recommended that you call it in your main loop.
147  /// \param[in] buf Location to copy the received message
148  /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
149  /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address
150  /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address
151  /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
152  /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
153  /// (not just those addressed to this node).
154  /// \return true if a valid message was copied to buf. False if
155  /// - 1. There was no message received and waiting to be collected, or
156  /// - 2. There was a message received but it was not addressed to this node, or
157  /// - 3. There was a correctly addressed message but it was a duplicate of an earlier correctly received message
158  bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL);
159 
160  /// Similar to recvfromAck(), this will block until either a valid message available for this node
161  /// or the timeout expires. Starts the receiver automatically.
162  /// You should be sure to call this function frequently enough to not miss any messages.
163  /// It is recommended that you call it in your main loop.
164  /// \param[in] buf Location to copy the received message
165  /// \param[in,out] len Available space in buf. Set to the actual number of octets copied.
166  /// \param[in] timeout Maximum time to wait in milliseconds
167  /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address
168  /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address
169  /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID
170  /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS
171  /// (not just those addressed to this node).
172  /// \return true if a valid message was copied to buf
173  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);
174 
175  /// Returns the number of retransmissions
176  /// we have had to send since starting or since the last call to resetRetransmissions().
177  /// \return The number of retransmissions since initialisation.
178  uint32_t retransmissions();
179 
180  /// Resets the count of the number of retransmissions
181  /// to 0.
182  void resetRetransmissions();
183 
184 protected:
185  /// Send an ACK for the message id to the given from address
186  /// Blocks until the ACK has been sent
187  void acknowledge(uint8_t id, uint8_t from);
188 
189  /// Checks whether the message currently in the Rx buffer is a new message, not previously received
190  /// based on the from address and the sequence. If it is new, it is acknowledged and returns true
191  /// \return true if there is a message received and it is a new message
192  bool haveNewMessage();
193 
194 private:
195  /// Count of retransmissions we have had to send
196  uint32_t _retransmissions;
197 
198  /// The last sequence number to be used
199  /// Defaults to 0
200  uint8_t _lastSequenceNumber;
201 
202  // Retransmit timeout (milliseconds)
203  /// Defaults to 200
204  uint16_t _timeout;
205 
206  // Retries (0 means one try only)
207  /// Defaults to 3
208  uint8_t _retries;
209 
210  /// Array of the last seen sequence number indexed by node address that sent it
211  /// It is used for duplicate detection. Duplicated messages are re-acknowledged when received
212  /// (this is generally due to lost ACKs, causing the sender to retransmit, even though we have already
213  /// received that message)
214  uint8_t _seenIds[256];
215 };
216 
217 /// @example rf22_reliable_datagram_client.pde
218 /// @example rf22_reliable_datagram_server.pde
219 
220 #endif
221 
RHDatagram::thisAddress
uint8_t thisAddress()
Definition: RHDatagram.cpp:77
RHDatagram::setHeaderFlags
void setHeaderFlags(uint8_t set, uint8_t clear=RH_FLAGS_NONE)
Definition: RHDatagram.cpp:97
RHReliableDatagram::setRetries
void setRetries(uint8_t retries)
Definition: RHReliableDatagram.cpp:36
RHReliableDatagram::sendtoWait
bool sendtoWait(uint8_t *buf, uint8_t len, uint8_t address)
Definition: RHReliableDatagram.cpp:48
RHReliableDatagram::recvfromAck
bool recvfromAck(uint8_t *buf, uint8_t *len, uint8_t *from=NULL, uint8_t *to=NULL, uint8_t *id=NULL, uint8_t *flags=NULL)
Definition: RHReliableDatagram.cpp:128
RHGenericDriver
Abstract base class for a RadioHead driver.
Definition: RHGenericDriver.h:41
RHDatagram::sendto
bool sendto(uint8_t *buf, uint8_t len, uint8_t address)
Definition: RHDatagram.cpp:33
RHDatagram::recvfrom
bool recvfrom(uint8_t *buf, uint8_t *len, uint8_t *from=NULL, uint8_t *to=NULL, uint8_t *id=NULL, uint8_t *flags=NULL)
Definition: RHDatagram.cpp:39
RHDatagram::waitAvailableTimeout
bool waitAvailableTimeout(uint16_t timeout)
Definition: RHDatagram.cpp:72
RHReliableDatagram::haveNewMessage
bool haveNewMessage()
RHReliableDatagram::retransmissions
uint32_t retransmissions()
Definition: RHReliableDatagram.cpp:186
RHReliableDatagram::resetRetransmissions
void resetRetransmissions()
Definition: RHReliableDatagram.cpp:191
RHDatagram::available
bool available()
Definition: RHDatagram.cpp:52
RHReliableDatagram::recvfromAckTimeout
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)
Definition: RHReliableDatagram.cpp:170
RHDatagram::setHeaderId
void setHeaderId(uint8_t id)
Definition: RHDatagram.cpp:92
RHReliableDatagram::retries
uint8_t retries()
Definition: RHReliableDatagram.cpp:42
RHReliableDatagram
RHDatagram subclass for sending addressed, acknowledged, retransmitted datagrams.
Definition: RHReliableDatagram.h:93
RHDatagram
Manager class for addressed, unreliable messages.
Definition: RHDatagram.h:45
RHReliableDatagram::RHReliableDatagram
RHReliableDatagram(RHGenericDriver &driver, uint8_t thisAddress=0)
Definition: RHReliableDatagram.cpp:18
RHDatagram::waitPacketSent
bool waitPacketSent()
Definition: RHDatagram.cpp:62
RHDatagram::_thisAddress
uint8_t _thisAddress
The address of this node.
Definition: RHDatagram.h:159
RHReliableDatagram::acknowledge
void acknowledge(uint8_t id, uint8_t from)
Definition: RHReliableDatagram.cpp:196
RHReliableDatagram::setTimeout
void setTimeout(uint16_t timeout)
Definition: RHReliableDatagram.cpp:30