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