RTC will not wake Arduino from sleep after apparently unrelated failure

I am using a Seeeduino Stalker and Adafruit Fona to collect stream height data from a Maxbotix Maxsonar unit and rainfall data from a rain gauge and upload these to data.sparkfun.com. Once per minute, the RTC on the Stalker sends an interrupt signal to pin 2, and the sketch checks to see if, according to the upload schedule, it’s time to send an upload, and if so, to send it. If the upload fails – and there are a variety of reasons it might – the Fona reboots and attempts it again, up to three times total. Having successfully completed the loop, the Stalker goes to sleep until the next RTC interrupt.

For the most part it works well. What is puzzling me is that when an upload fails – even if the second or third attempt during the same loop is successful – the loop proceeds as one would expect except that after sleeping, the RTC interrupt will not wake the Stalker (though the rain gauge interrupt on pin 3 will). I am not a very experienced programmer, but I cannot see where the upload failure would connect with the RTC wake function.

I apologize that the code is rather long – too long to include in the text of this post, so it’s attached. Any advice appreciated. Many thanks for taking the time to have a look.

hidrosonico.txt (16.2 KB)

 delay(500); // Give Serial a chance to catch up before sleep

That is the wrong way to do that. Serial.flush() will make sure the outgoing serial buffer is empty before it returns. As soon as it returns, you can go to sleep.

 // To aid debugging, we'll do every 5 minutes instead
 if(now.minute() == 0 || now.minute() == 5 || now.minute() == 10 || 
 now.minute() == 15 || now.minute() == 20 || now.minute() == 25 || 
 now.minute() == 30 || now.minute() == 35 || now.minute() == 40 || 
 now.minute() == 45 || now.minute() == 50 || now.minute() == 55)

Or, simply

if(now.minute() % 5 == 0)
 currentHour = (byte)now.hour(); // Cast the time and date values
 currentMinute = (byte)now.minute(); // as integers so we can manipulate
 currentDay = (byte)now.date(); // them more easily later.
 currentMonth = (byte)now.month();
 currentYear = (int)now.year();

The explicit casts are not needed. The comments are wrong.

 int streamHeight = takeReading(); // Take a reading

Good thing you added a comment. I'd have never guessed what that function did, otherwise.

I got tired of reading unindented code at that point.

I can't figure out where the blocks start and end when there is no indenting done.

I did it in Codebender, where it is indented nicely, but the copy/paste did not preserve these. Sorry you found it difficult to read.

I am erring on the side of overcommenting the code because the intent is to leave this with people who are not currently proficient in coding. The hope is that they will be able to pick apart the code both so that they can understand and modify the tool and use it as a learning aid.

Could you expand on why the casts are not necessary? I had problems before I did that because functions that expected an integer were choking on the time_t.

Here, perhaps this will be easier to follow:

https://codebender.cc/sketch:117279

Could you expand on why the casts are not necessary?

There are implicit casts and explicit casts.

An implicit cast occurs when a value is assigned to a different kind of variable, as in:

int p = 3.6 / 1.2;

An explicit cast happens when you type the parentheses with a type between them. An explicit cast is generally only needed when the types are different kinds of pointers.

I had problems before I did that because functions that expected an integer were choking on the time_t.

Scalars? Or arrays?

I don't see anything in the code that would explain the failure to wake up after failing to send a message, unless it is simply the time it takes to fail to send a message.

I see. So because currentHour is already a byte, currentHour = (byte)now.hour(); is redundant?

Most commonly, the Rip Van Winkles come on after the first upload attempt fails and a subsequent one succeeds, so that routine should not end any differently than on a successful initial send. I've looked and looked at the code that identifies and responds to a failed upload and I don't see anything that ought to have anything to do with the RTC interrupt.

Thanks for the tips on % 5 and Serial.flush().

I don’t see anything in the code that would explain the failure to wake up after failing to send a message, unless it is simply the time it takes to fail to send a message.

On reflection, I think that is exactly the problem: RTC.clearINTStatus(); appears at the beginning of loop(). That’s normally fine, because even when uploading, the loop doesn’t generally take more than a minute to complete. But if it has to upload more than once, that may take more than a minute, and the second interrupt on the RTC doesn’t get cleared, so it doesn’t trigger again.

I moved it to the bottom of the loop, right before sleep. It often takes a while for the error condition to come up, but I feel pretty confident that this was the problem. Thanks!