V148

Fork of RadioHead-148 by David Rimer

Revision:
0:ab4e012489ef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RHReliableDatagram.cpp	Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,189 @@
+// RHReliableDatagram.cpp
+//
+// Define addressed datagram
+// 
+// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers 
+// (see http://www.hoperf.com)
+// RHDatagram will be received only by the addressed node or all nodes within range if the 
+// to address is RH_BROADCAST_ADDRESS
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHReliableDatagram.cpp,v 1.14 2015/08/13 02:45:47 mikem Exp $
+
+#include <RHReliableDatagram.h>
+
+////////////////////////////////////////////////////////////////////
+// Constructors
+RHReliableDatagram::RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress) 
+    : RHDatagram(driver, thisAddress)
+{
+    _retransmissions = 0;
+    _lastSequenceNumber = 0;
+    _timeout = RH_DEFAULT_TIMEOUT;
+    _retries = RH_DEFAULT_RETRIES;
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+void RHReliableDatagram::setTimeout(uint16_t timeout)
+{
+    _timeout = timeout;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHReliableDatagram::setRetries(uint8_t retries)
+{
+    _retries = retries;
+}
+
+////////////////////////////////////////////////////////////////////
+uint8_t RHReliableDatagram::retries()
+{
+    return _retries;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address)
+{
+    // Assemble the message
+    uint8_t thisSequenceNumber = ++_lastSequenceNumber;
+    uint8_t retries = 0;
+    while (retries++ <= _retries)
+    {
+	setHeaderId(thisSequenceNumber);
+	setHeaderFlags(RH_FLAGS_NONE, RH_FLAGS_ACK); // Clear the ACK flag
+	sendto(buf, len, address);
+	waitPacketSent();
+
+	// Never wait for ACKS to broadcasts:
+	if (address == RH_BROADCAST_ADDRESS)
+	    return true;
+
+	if (retries > 1)
+	    _retransmissions++;
+	unsigned long thisSendTime = millis(); // Timeout does not include original transmit time
+
+	// Compute a new timeout, random between _timeout and _timeout*2
+	// This is to prevent collisions on every retransmit
+	// if 2 nodes try to transmit at the same time
+#if (RH_PLATFORM == RH_PLATFORM_RASPI) // use standard library random(), bugs in random(min, max)
+	uint16_t timeout = _timeout + (_timeout * (random() & 0xFF) / 256);
+#elif (RH_PLATFORM == RH_PLATFORM_MBED)
+	uint16_t timeout = _timeout + (_timeout * (rand() & 0xFF) / 256);
+#else
+	uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256);
+#endif
+	int32_t timeLeft;
+        while ((timeLeft = timeout - (millis() - thisSendTime)) > 0)
+	{
+	    if (waitAvailableTimeout(timeLeft))
+	    {
+		uint8_t from, to, id, flags;
+		if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message
+		{
+		    // Now have a message: is it our ACK?
+		    if (   from == address 
+			   && to == _thisAddress 
+			   && (flags & RH_FLAGS_ACK) 
+			   && (id == thisSequenceNumber))
+		    {
+			// Its the ACK we are waiting for
+			return true;
+		    }
+		    else if (   !(flags & RH_FLAGS_ACK)
+				&& (id == _seenIds[from]))
+		    {
+			// This is a request we have already received. ACK it again
+			acknowledge(id, from);
+		    }
+		    // Else discard it
+		}
+	    }
+	    // Not the one we are waiting for, maybe keep waiting until timeout exhausted
+	    YIELD;
+	}
+	// Timeout exhausted, maybe retry
+	YIELD;
+    }
+    // Retries exhausted
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHReliableDatagram::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{  
+    uint8_t _from;
+    uint8_t _to;
+    uint8_t _id;
+    uint8_t _flags;
+    // Get the message before its clobbered by the ACK (shared rx and tx buffer in some drivers
+    if (available() && recvfrom(buf, len, &_from, &_to, &_id, &_flags))
+    {
+	// Never ACK an ACK
+	if (!(_flags & RH_FLAGS_ACK))
+	{
+	    // Its a normal message for this node, not an ACK
+	    if (_to != RH_BROADCAST_ADDRESS)
+	    {
+		// Its not a broadcast, so ACK it
+		// Acknowledge message with ACK set in flags and ID set to received ID
+		acknowledge(_id, _from);
+	    }
+	    // If we have not seen this message before, then we are interested in it
+	    if (_id != _seenIds[_from])
+	    {
+		if (from)  *from =  _from;
+		if (to)    *to =    _to;
+		if (id)    *id =    _id;
+		if (flags) *flags = _flags;
+		_seenIds[_from] = _id;
+		return true;
+	    }
+	    // Else just re-ack it and wait for a new one
+	}
+    }
+    // No message for us available
+    return false;
+}
+
+bool RHReliableDatagram::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags)
+{
+    unsigned long starttime = millis();
+    int32_t timeLeft;
+    while ((timeLeft = timeout - (millis() - starttime)) > 0)
+    {
+	if (waitAvailableTimeout(timeLeft))
+	{
+	    if (recvfromAck(buf, len, from, to, id, flags))
+		return true;
+	}
+	YIELD;
+    }
+    return false;
+}
+
+uint32_t RHReliableDatagram::retransmissions()
+{
+    return _retransmissions;
+}
+
+void RHReliableDatagram::resetRetransmissions()
+{
+    _retransmissions = 0;
+}
+ 
+void RHReliableDatagram::acknowledge(uint8_t id, uint8_t from)
+{
+    setHeaderId(id);
+    setHeaderFlags(RH_FLAGS_ACK);
+    // We would prefer to send a zero length ACK,
+    // but if an RH_RF22 receives a 0 length message with a CRC error, it will never receive
+    // a 0 length message again, until its reset, which makes everything hang :-(
+    // So we send an ACK of 1 octet
+    // REVISIT: should we send the RSSI for the information of the sender?
+    uint8_t ack = '!';
+    sendto(&ack, sizeof(ack), from); 
+    waitPacketSent();
+}
+