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