So, there is what I found and what I did.
In two last posts from
jgilbert I saw the same problem and thinking what I found.
Examining the code and putting all RFM69 logic on paper like diagram
I found that receiveDone do not work as expected.
The main problem what causes the network hanging is enabling interrupts (for very short time of course).
Waiting for free network receiveDone() is called (with enabled interrupts!)
while (!canSend()) receiveDone();
and if in that time some other message is incoming in gateway node problem happens as radio gets receive state (and last one received message is not cleared correctly with next receiveDone->receiveBegin execution, also).
To solve this there are 2 ways:
a) improve receiveDone and think about interupts
b) change/improve sendACK.
I went for b), as I didn't liked that sendACK clears incoming message and can destroy SENDERID.
With improved code below sendACK can be executed immediately after receiveDone w/o destroying message and getting some extra ms what is important for sleeping low power nodes waiting for ACK(avoiding read out data from radio before execute sendACK as now).
RFM69.cpp
void RFM69::sendACK(const void* buffer, byte bufferSize) {
setMode(RF69_MODE_RX); //Switching from STANDBY to RX before TX
int _RSSI = RSSI; //save payload received RSSI value
bool canSendACK = false;
long now = millis();
while (millis()-now < ACK_CSMA_LIMIT_MS) //wait for free network the same time as sender waits for ACK
{
if (readRSSI() < CSMA_LIMIT) //if signal weaker than -90dBm(CSMA_LIMIT) is detected channel should be free
{
canSendACK = true;
break;
}
}
if (canSendACK) // channel is free let's send ACK
{
// Serial.print("ACK sent:");Serial.print(millis()-now, DEC); Serial.print("ms;RSSI:");Serial.println(readRSSI(), DEC); Serial.flush();
sendFrame(SENDERID, buffer, bufferSize, false, true);
}
RSSI = _RSSI; //restore payload RSSI
}
RFM69.h (add first line, change second)
#define ACK_CSMA_LIMIT_MS 40
bool sendWithRetry(byte toAddress, const void* buffer, byte bufferSize, byte retries=2, byte retryWaitTime=ACK_CSMA_LIMIT_MS); //40ms roundtrip req for 61byte packets
I will explain what I did and why:
-in
RFM69.h added new defined value ACK_CSMA_LIMIT_MS which is time in ms before send next one message if ACK wasn't received.
-use this parameter instead of manual value in sendWithRetry() procedure, because it will be used later in some other place
RFM69.cpp- setting mode back to RX (without this sending do not work). Why changing status from STANDBY to TX in sendFrame do not allow to send, I didn't figured. Seems it want's RX before or some timing issues. The correct place for it is one line before sendFrame, but in that case it is not working(seems some time is needed to switch), tried to add "Wait for ModeReady" w/o success
- keeping RSSI, as we will do measurement for current network and message received RSSI should be kept
- in while loop we are checking free network conditions
ONLY ACK_CSMA_LIMIT_MS time, as we do not need to do it longer because sendWithRetry only waits for ACK this amount of time. (In current version it is to long also, no need to wait for free network to sendACK if sender waited only 40ms for answer)
- sending ACK
This version works very good for me, ACK times are improved and tracing sendACK observed that in most cases while loop executes immediately (0ms). Super!
You can get full changed code and look at changes in github:
https://github.com/openminihub/RFM69EDIT: Bad news, it hanged again...
added writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
before sendFrame. Now going to sleep, let's see what morning will tell.