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