mcci-catena / arduino-lmic

LoraWAN-MAC-in-C library, adapted to run under the Arduino environment
https://forum.mcci.io/c/device-software/arduino-lmic/
MIT License
634 stars 208 forks source link

sleeping for longer period (> 15 minutes) can cause delayed transmission #926

Open dhineshkumarmcci opened 1 year ago

dhineshkumarmcci commented 1 year ago

Issue description

There are some major problems with the Arduino LMIC if you sleep the system for an extended period of time.

These implementation limitations mean that the LMIC can get a fairly crazy ostime value if the CPU has slept. This can (in turn) cause the LMIC to misinterpret globalDutyAvail as being in the future rather than in the past (as it really will be).

This will lead to delayed transmit of uplink messages rather than sending at the time the user called the LMIC to send the message.

This issue can be seen because of below reasons.

  1. If no transmission for around 9 hours, the time calculations will overflow, because ostime_t is signed and the forward/past sense will flip.
  2. If the os_gettime() is not called once every 17 minutes, time values will be wrong, and this may also cause timestamps to flip between "past" and "future".

Environment

Quick fix

We have a quick fix by adding an API in the top-level application to fix LMIC time calculations which helps to sync the globalDutyAvail with current LMIC ostime. This will help to avoid the delayed transmission. Calling this API immediate after waking up from long sleep will help to do transmission on-time.

Please find the API below:

// This fix only works in the specific case where you're sure that you've been sleeping
// so long that any time delays that the LMIC is worried about are in the past.
//
void fixLmicTimeCalculationAfterWakeup(void) {
    ostime_t const now = os_getTime();
    // just tell the LMIC that we're available *now*.
    LMIC.globalDutyAvail = now;
    // no need to randomize 
    // for EU-like, we need to reset all the channel avail times to "now"
#if CFG_LMIC_EU_like
    for (unsigned i = 0; i < MAX_BANDS; ++i) {
        LMIC.bands[i].avail = now;
#endif
    }
}

Updated by @terrillmoore -- splitting part of this out to a separate issue #927. The fix above doesn't help with #927, which is really independent of sleep.

uhi22 commented 1 year ago

The quickfix above has the drawback, that it allows immediate transmission after sleep. For the case, that the avail time is intentionally more in the future, this would lead to a too-early transmit. I propose a more complete solution, which consists of two steps: 1. after sleep, add the sleep time to the timer, to keep track of the real elapsed time. This was proposed in https://www.thethingsnetwork.org/forum/t/lmic-plus-rfm95w-staying-awake-for-long-periods/7506/44 The hacky version for 8 seconds arduino-sleep: //Give the AVR back the slept time back (simple version) cli(); timer0_overflow_count += 8 64 clockCyclesPerMicrosecond(); //give back 8 seconds of sleep sei(); os_getTime(); / VERY IMPORTANT after sleep to update os_time and not cause txbeg and os_time out of sync which causes send delays with the RFM95 on and eating power /

Step 2: Make sure, that the avail-variables do not run too much into the past. Pseudocode: if avail is in the past, then avail = now. I referenced my bugfix proposal in #934.