V148
Fork of RadioHead-148 by
RHReliableDatagram.cpp@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.cpp |
davidr99 | 0:ab4e012489ef | 2 | // |
davidr99 | 0:ab4e012489ef | 3 | // Define addressed datagram |
davidr99 | 0:ab4e012489ef | 4 | // |
davidr99 | 0:ab4e012489ef | 5 | // Part of the Arduino RH library for operating with HopeRF RH compatible transceivers |
davidr99 | 0:ab4e012489ef | 6 | // (see http://www.hoperf.com) |
davidr99 | 0:ab4e012489ef | 7 | // RHDatagram will be received only by the addressed node or all nodes within range if the |
davidr99 | 0:ab4e012489ef | 8 | // to address is RH_BROADCAST_ADDRESS |
davidr99 | 0:ab4e012489ef | 9 | // |
davidr99 | 0:ab4e012489ef | 10 | // Author: Mike McCauley (mikem@airspayce.com) |
davidr99 | 0:ab4e012489ef | 11 | // Copyright (C) 2011 Mike McCauley |
davidr99 | 0:ab4e012489ef | 12 | // $Id: RHReliableDatagram.cpp,v 1.14 2015/08/13 02:45:47 mikem Exp $ |
davidr99 | 0:ab4e012489ef | 13 | |
davidr99 | 0:ab4e012489ef | 14 | #include <RHReliableDatagram.h> |
davidr99 | 0:ab4e012489ef | 15 | |
davidr99 | 0:ab4e012489ef | 16 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 17 | // Constructors |
davidr99 | 0:ab4e012489ef | 18 | RHReliableDatagram::RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress) |
davidr99 | 0:ab4e012489ef | 19 | : RHDatagram(driver, thisAddress) |
davidr99 | 0:ab4e012489ef | 20 | { |
davidr99 | 0:ab4e012489ef | 21 | _retransmissions = 0; |
davidr99 | 0:ab4e012489ef | 22 | _lastSequenceNumber = 0; |
davidr99 | 0:ab4e012489ef | 23 | _timeout = RH_DEFAULT_TIMEOUT; |
davidr99 | 0:ab4e012489ef | 24 | _retries = RH_DEFAULT_RETRIES; |
davidr99 | 0:ab4e012489ef | 25 | } |
davidr99 | 0:ab4e012489ef | 26 | |
davidr99 | 0:ab4e012489ef | 27 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 28 | // Public methods |
davidr99 | 0:ab4e012489ef | 29 | void RHReliableDatagram::setTimeout(uint16_t timeout) |
davidr99 | 0:ab4e012489ef | 30 | { |
davidr99 | 0:ab4e012489ef | 31 | _timeout = timeout; |
davidr99 | 0:ab4e012489ef | 32 | } |
davidr99 | 0:ab4e012489ef | 33 | |
davidr99 | 0:ab4e012489ef | 34 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 35 | void RHReliableDatagram::setRetries(uint8_t retries) |
davidr99 | 0:ab4e012489ef | 36 | { |
davidr99 | 0:ab4e012489ef | 37 | _retries = retries; |
davidr99 | 0:ab4e012489ef | 38 | } |
davidr99 | 0:ab4e012489ef | 39 | |
davidr99 | 0:ab4e012489ef | 40 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 41 | uint8_t RHReliableDatagram::retries() |
davidr99 | 0:ab4e012489ef | 42 | { |
davidr99 | 0:ab4e012489ef | 43 | return _retries; |
davidr99 | 0:ab4e012489ef | 44 | } |
davidr99 | 0:ab4e012489ef | 45 | |
davidr99 | 0:ab4e012489ef | 46 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 47 | bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address) |
davidr99 | 0:ab4e012489ef | 48 | { |
davidr99 | 0:ab4e012489ef | 49 | // Assemble the message |
davidr99 | 0:ab4e012489ef | 50 | uint8_t thisSequenceNumber = ++_lastSequenceNumber; |
davidr99 | 0:ab4e012489ef | 51 | uint8_t retries = 0; |
davidr99 | 0:ab4e012489ef | 52 | while (retries++ <= _retries) |
davidr99 | 0:ab4e012489ef | 53 | { |
davidr99 | 0:ab4e012489ef | 54 | setHeaderId(thisSequenceNumber); |
davidr99 | 0:ab4e012489ef | 55 | setHeaderFlags(RH_FLAGS_NONE, RH_FLAGS_ACK); // Clear the ACK flag |
davidr99 | 0:ab4e012489ef | 56 | sendto(buf, len, address); |
davidr99 | 0:ab4e012489ef | 57 | waitPacketSent(); |
davidr99 | 0:ab4e012489ef | 58 | |
davidr99 | 0:ab4e012489ef | 59 | // Never wait for ACKS to broadcasts: |
davidr99 | 0:ab4e012489ef | 60 | if (address == RH_BROADCAST_ADDRESS) |
davidr99 | 0:ab4e012489ef | 61 | return true; |
davidr99 | 0:ab4e012489ef | 62 | |
davidr99 | 0:ab4e012489ef | 63 | if (retries > 1) |
davidr99 | 0:ab4e012489ef | 64 | _retransmissions++; |
davidr99 | 0:ab4e012489ef | 65 | unsigned long thisSendTime = millis(); // Timeout does not include original transmit time |
davidr99 | 0:ab4e012489ef | 66 | |
davidr99 | 0:ab4e012489ef | 67 | // Compute a new timeout, random between _timeout and _timeout*2 |
davidr99 | 0:ab4e012489ef | 68 | // This is to prevent collisions on every retransmit |
davidr99 | 0:ab4e012489ef | 69 | // if 2 nodes try to transmit at the same time |
davidr99 | 0:ab4e012489ef | 70 | #if (RH_PLATFORM == RH_PLATFORM_RASPI) // use standard library random(), bugs in random(min, max) |
davidr99 | 0:ab4e012489ef | 71 | uint16_t timeout = _timeout + (_timeout * (random() & 0xFF) / 256); |
davidr99 | 0:ab4e012489ef | 72 | #elif (RH_PLATFORM == RH_PLATFORM_MBED) |
davidr99 | 0:ab4e012489ef | 73 | uint16_t timeout = _timeout + (_timeout * (rand() & 0xFF) / 256); |
davidr99 | 0:ab4e012489ef | 74 | #else |
davidr99 | 0:ab4e012489ef | 75 | uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256); |
davidr99 | 0:ab4e012489ef | 76 | #endif |
davidr99 | 0:ab4e012489ef | 77 | int32_t timeLeft; |
davidr99 | 0:ab4e012489ef | 78 | while ((timeLeft = timeout - (millis() - thisSendTime)) > 0) |
davidr99 | 0:ab4e012489ef | 79 | { |
davidr99 | 0:ab4e012489ef | 80 | if (waitAvailableTimeout(timeLeft)) |
davidr99 | 0:ab4e012489ef | 81 | { |
davidr99 | 0:ab4e012489ef | 82 | uint8_t from, to, id, flags; |
davidr99 | 0:ab4e012489ef | 83 | if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message |
davidr99 | 0:ab4e012489ef | 84 | { |
davidr99 | 0:ab4e012489ef | 85 | // Now have a message: is it our ACK? |
davidr99 | 0:ab4e012489ef | 86 | if ( from == address |
davidr99 | 0:ab4e012489ef | 87 | && to == _thisAddress |
davidr99 | 0:ab4e012489ef | 88 | && (flags & RH_FLAGS_ACK) |
davidr99 | 0:ab4e012489ef | 89 | && (id == thisSequenceNumber)) |
davidr99 | 0:ab4e012489ef | 90 | { |
davidr99 | 0:ab4e012489ef | 91 | // Its the ACK we are waiting for |
davidr99 | 0:ab4e012489ef | 92 | return true; |
davidr99 | 0:ab4e012489ef | 93 | } |
davidr99 | 0:ab4e012489ef | 94 | else if ( !(flags & RH_FLAGS_ACK) |
davidr99 | 0:ab4e012489ef | 95 | && (id == _seenIds[from])) |
davidr99 | 0:ab4e012489ef | 96 | { |
davidr99 | 0:ab4e012489ef | 97 | // This is a request we have already received. ACK it again |
davidr99 | 0:ab4e012489ef | 98 | acknowledge(id, from); |
davidr99 | 0:ab4e012489ef | 99 | } |
davidr99 | 0:ab4e012489ef | 100 | // Else discard it |
davidr99 | 0:ab4e012489ef | 101 | } |
davidr99 | 0:ab4e012489ef | 102 | } |
davidr99 | 0:ab4e012489ef | 103 | // Not the one we are waiting for, maybe keep waiting until timeout exhausted |
davidr99 | 0:ab4e012489ef | 104 | YIELD; |
davidr99 | 0:ab4e012489ef | 105 | } |
davidr99 | 0:ab4e012489ef | 106 | // Timeout exhausted, maybe retry |
davidr99 | 0:ab4e012489ef | 107 | YIELD; |
davidr99 | 0:ab4e012489ef | 108 | } |
davidr99 | 0:ab4e012489ef | 109 | // Retries exhausted |
davidr99 | 0:ab4e012489ef | 110 | return false; |
davidr99 | 0:ab4e012489ef | 111 | } |
davidr99 | 0:ab4e012489ef | 112 | |
davidr99 | 0:ab4e012489ef | 113 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 114 | bool RHReliableDatagram::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) |
davidr99 | 0:ab4e012489ef | 115 | { |
davidr99 | 0:ab4e012489ef | 116 | uint8_t _from; |
davidr99 | 0:ab4e012489ef | 117 | uint8_t _to; |
davidr99 | 0:ab4e012489ef | 118 | uint8_t _id; |
davidr99 | 0:ab4e012489ef | 119 | uint8_t _flags; |
davidr99 | 0:ab4e012489ef | 120 | // Get the message before its clobbered by the ACK (shared rx and tx buffer in some drivers |
davidr99 | 0:ab4e012489ef | 121 | if (available() && recvfrom(buf, len, &_from, &_to, &_id, &_flags)) |
davidr99 | 0:ab4e012489ef | 122 | { |
davidr99 | 0:ab4e012489ef | 123 | // Never ACK an ACK |
davidr99 | 0:ab4e012489ef | 124 | if (!(_flags & RH_FLAGS_ACK)) |
davidr99 | 0:ab4e012489ef | 125 | { |
davidr99 | 0:ab4e012489ef | 126 | // Its a normal message for this node, not an ACK |
davidr99 | 0:ab4e012489ef | 127 | if (_to != RH_BROADCAST_ADDRESS) |
davidr99 | 0:ab4e012489ef | 128 | { |
davidr99 | 0:ab4e012489ef | 129 | // Its not a broadcast, so ACK it |
davidr99 | 0:ab4e012489ef | 130 | // Acknowledge message with ACK set in flags and ID set to received ID |
davidr99 | 0:ab4e012489ef | 131 | acknowledge(_id, _from); |
davidr99 | 0:ab4e012489ef | 132 | } |
davidr99 | 0:ab4e012489ef | 133 | // If we have not seen this message before, then we are interested in it |
davidr99 | 0:ab4e012489ef | 134 | if (_id != _seenIds[_from]) |
davidr99 | 0:ab4e012489ef | 135 | { |
davidr99 | 0:ab4e012489ef | 136 | if (from) *from = _from; |
davidr99 | 0:ab4e012489ef | 137 | if (to) *to = _to; |
davidr99 | 0:ab4e012489ef | 138 | if (id) *id = _id; |
davidr99 | 0:ab4e012489ef | 139 | if (flags) *flags = _flags; |
davidr99 | 0:ab4e012489ef | 140 | _seenIds[_from] = _id; |
davidr99 | 0:ab4e012489ef | 141 | return true; |
davidr99 | 0:ab4e012489ef | 142 | } |
davidr99 | 0:ab4e012489ef | 143 | // Else just re-ack it and wait for a new one |
davidr99 | 0:ab4e012489ef | 144 | } |
davidr99 | 0:ab4e012489ef | 145 | } |
davidr99 | 0:ab4e012489ef | 146 | // No message for us available |
davidr99 | 0:ab4e012489ef | 147 | return false; |
davidr99 | 0:ab4e012489ef | 148 | } |
davidr99 | 0:ab4e012489ef | 149 | |
davidr99 | 0:ab4e012489ef | 150 | bool RHReliableDatagram::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) |
davidr99 | 0:ab4e012489ef | 151 | { |
davidr99 | 0:ab4e012489ef | 152 | unsigned long starttime = millis(); |
davidr99 | 0:ab4e012489ef | 153 | int32_t timeLeft; |
davidr99 | 0:ab4e012489ef | 154 | while ((timeLeft = timeout - (millis() - starttime)) > 0) |
davidr99 | 0:ab4e012489ef | 155 | { |
davidr99 | 0:ab4e012489ef | 156 | if (waitAvailableTimeout(timeLeft)) |
davidr99 | 0:ab4e012489ef | 157 | { |
davidr99 | 0:ab4e012489ef | 158 | if (recvfromAck(buf, len, from, to, id, flags)) |
davidr99 | 0:ab4e012489ef | 159 | return true; |
davidr99 | 0:ab4e012489ef | 160 | } |
davidr99 | 0:ab4e012489ef | 161 | YIELD; |
davidr99 | 0:ab4e012489ef | 162 | } |
davidr99 | 0:ab4e012489ef | 163 | return false; |
davidr99 | 0:ab4e012489ef | 164 | } |
davidr99 | 0:ab4e012489ef | 165 | |
davidr99 | 0:ab4e012489ef | 166 | uint32_t RHReliableDatagram::retransmissions() |
davidr99 | 0:ab4e012489ef | 167 | { |
davidr99 | 0:ab4e012489ef | 168 | return _retransmissions; |
davidr99 | 0:ab4e012489ef | 169 | } |
davidr99 | 0:ab4e012489ef | 170 | |
davidr99 | 0:ab4e012489ef | 171 | void RHReliableDatagram::resetRetransmissions() |
davidr99 | 0:ab4e012489ef | 172 | { |
davidr99 | 0:ab4e012489ef | 173 | _retransmissions = 0; |
davidr99 | 0:ab4e012489ef | 174 | } |
davidr99 | 0:ab4e012489ef | 175 | |
davidr99 | 0:ab4e012489ef | 176 | void RHReliableDatagram::acknowledge(uint8_t id, uint8_t from) |
davidr99 | 0:ab4e012489ef | 177 | { |
davidr99 | 0:ab4e012489ef | 178 | setHeaderId(id); |
davidr99 | 0:ab4e012489ef | 179 | setHeaderFlags(RH_FLAGS_ACK); |
davidr99 | 0:ab4e012489ef | 180 | // We would prefer to send a zero length ACK, |
davidr99 | 0:ab4e012489ef | 181 | // but if an RH_RF22 receives a 0 length message with a CRC error, it will never receive |
davidr99 | 0:ab4e012489ef | 182 | // a 0 length message again, until its reset, which makes everything hang :-( |
davidr99 | 0:ab4e012489ef | 183 | // So we send an ACK of 1 octet |
davidr99 | 0:ab4e012489ef | 184 | // REVISIT: should we send the RSSI for the information of the sender? |
davidr99 | 0:ab4e012489ef | 185 | uint8_t ack = '!'; |
davidr99 | 0:ab4e012489ef | 186 | sendto(&ack, sizeof(ack), from); |
davidr99 | 0:ab4e012489ef | 187 | waitPacketSent(); |
davidr99 | 0:ab4e012489ef | 188 | } |
davidr99 | 0:ab4e012489ef | 189 |