Adafruit Feather 32u4 LoRa - long transmission time after deep sleep

I’ve got some Adafruit 32u4 LoRa feathers up and running.

The sketch is based on the ttn_otaa example (arduino-lmic/matthijskooijman):

    ... 
        case EV_TXCOMPLETE:      
          if (LMIC.txrxFlags & TXRX_ACK)
           ...
          if (LMIC.dataLen) {
            ...
          }
          sleep();
          // Start job again
          do_send(&sendjob);
    ...

In sleep() I’m using LowPower.h from rocketscream but also with nativesleep commands I got the same results.

Everything is fine but after putting the nodes in deep sleep the following transmission last more than 6 seconds. After a simple delay instead of deep sleep the transmission lasts about 2.5 seconds as expected.

Any ideas how to speed that up? About 4 additional seconds consume A LOT of extra battery current!

Does it send right away, and then it takes longer for the event to fire? Or any chance it’s doing an OTAA join again?

No (re-)join! It just takes extra time. I put the following int do_send(…)

startsending=millis();
LMIC_setTxData2(1, payload, PAYLOAD_LENGTH, 0);

And then in onEvent(…)

case EV_TXCOMPLETE:
  PRINT(F(" ok "));PRINT(millis()-startsending); PRINTLN(F("ms"));

Had a look with an oscilloscope and there is “extra time” before the TX-peak.

Then I assume the library does not know time has passed during sleep…

But it’s always the same delay. Doesn’t matter how long the node is sleeping (20seconds - minutes).

The library will not allow you to send more often than the duty cycle allows for. Without any sleep, you’ll see it will also be delaying subsequent transmissions until it is allowed to transmit again. If you first use some delay (for testing) and only then deep sleep, iI guess you won’t see any delay when deep sleep ends.

Also:

And:

This implies the library is delaying even more than the duty cycle would enforce.

So, if it does not know time has passed while sleeping, it will keep delaying after sleep, I’m afraid.

Thanks Arjan! That makes sense to me.

But a first quick test with reducing DNW2_SAFETY_ZONE to some milliseconds makes no difference. Is it possible / necessary to adjust the osticks after deep sleep?

I’ve no idea. Also, as a test, I’d first try to delay the deep sleep to make sure this is indeed causing this.

And you might want to read the full topic I linked to. Like:

1 Like

Ok. I did a quick test with the suggested

  delay(3000);
  sleep();
  do_send(&sendjob);

and the event fires instantly.

So I have to do some reading (thank You for the links) and try tomorrow. Thanks!

2 Likes

I’m not sure if there is any relation with the issue described in this topic but I did notice the following with the LoRa 32U4 from BSFrance:

When powering up the module (with OLED display connected) I instantly see activity on the display and the module instantly starts the OTAA join procedure.

But when I press the reset button the module starts doing ‘something’, the white LED lights up intermittently and it then takes some 5 to 7 seconds (not exactly timed) before the actual reset takes place. Powering up the module does not give delay.

Why is this happening?

Couldn’t resist :slight_smile:

After reading the linked infos I added

extern volatile unsigned long timer0_overflow_count;

to my sketch and after the deep sleep I inserted

timer0_overflow_count += 3E6;

Thats it! timer0_overflow_count comes from wiring.c in the arduino core and is used for micros().
Adding at least 3E6 microseconds did the job. Could be adjusted depending on the real sleep time.

Sounds so easy - did I miss something? Maybe there will be some trouble when micros rollover.

With this modification my node now needs less than three seconds for wake up, doing some measuring, sending data and going to deep sleep again.

4 Likes

One of the LMIC framework’s feature is that it schedules so called “jobs”. If you use “sleep” statements in your code, the LMIC framework can no longer fulfill this task of timely scheduling your jobs.

So just don’t use “sleep” in your sketches!

If you want to investigate and change the power consumption while the LMIC frameworks time of inactivity (sleep), you need to change the LMIC framework implementation itself. Check file https://github.com/matthijskooijman/arduino-lmic/blob/master/src/hal/hal.cpp and search for “delay” and you will find it in this method:

void hal_waitUntil (u4_t time) {
    s4_t delta = delta_time(time);
    // From delayMicroseconds docs: Currently, the largest value that
    // will produce an accurate delay is 16383.
    while (delta > (16000 / US_PER_OSTICK)) {
        delay(16);
        delta -= (16000 / US_PER_OSTICK);
    }
    if (delta > 0)
        delayMicroseconds(delta * US_PER_OSTICK);
}

I think this would be the place to add some native deep sleeping code.

Hi Wolfgang,

sorry, but I can´t agree / maybe I didn`t understand you.

In real world use cases deep sleep seems to be a must-have. We typically use battery driven nodes where power consumption is a very big issue. Every µA we can save is important. Maybe you might look at Full Arduino Mini LoraWAN and 1.3uA Sleep Mode for example.

As far as I understood the best place to do deep sleep in a sketch is at the end of the EV_TXCOMPLETE event. Here all the library internal scheduling is done. Typically the deep sleep function is placed just before schdeuling the next job.

So I think there is no need to change anything in the LMIC library.

As Matthijs stated in LMiC’s TX_COMPLETE event takes 20-30 seconds to fire we just have a problem because the micros also “stop” while the node is sleeping. So taking care of the duty cycle is delayed.

He suggests to adjust the Arduino internal micros() function.The code snippet I posted is just a try to do this.

In real world use cases deep sleep seems to be a must-have.

I totally agree.

But, IMO with LMIC there is a framework in place that is based on jobs, that are executed either immediately or after a defined delay:

from oslmic.h
void os_setCallback (xref2osjob_t job, osjobcb_t cb);
void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);

In general, the loop function in sketches using LMIC looks like this:

void loop() {
    os_runloop_once();
}

Let’s say if you want to read the value of a sensor every 120 seconds, then you just register a new job (os_setTimedCallback) with a planned execution time of now() + 120 seconds.

If you send the arduino to sleep in some place of your sketch, then you prevent the LMIC framework to timely execute timed callbacks. Your sleep statements interfere with the sleep activities of the LMIC framework. So because of this I do not recommend to do any delay/sleep activities in the code.

If there still is some active waiting for things to happen in the LMIC framework for Arduino, then IMO as it is implemented in a suboptimal manner or because it is difficult on the Arduino platform. So I still think it should be tried to fix it in the LMIC implementation for Arduino.

I.m.o. in low-power deep sleep situations anything related to handling (timing of) sleep periods should not be handled by the LMIC framework whatsoever.

When the MCU/device is woken up from sleep LMIC should be used to do it’s work as usual, but only within the time window that the MCU is up (not sleeping). And the MCU should wait for important LMIC events to complete before entering sleep again (where neccesary).
New LMIC related jobs like sending a message should be scheduled (to be serviced in that same time window) only after the MCU has woken up from sleep again.

This decouples the (time) management of sleep from the LMIC timing stuff that is required for proper handling of the LoRaWAN protocol.

2 Likes

the module enters bootmode because it doesn’t have a ‘real’ uart … that’s the same for the TTN UNO and the TTN node

1 Like

I guess it’s the same behavior as RST double tap on Adafruit LoRa32u4

Manually bootloading

If you ever get in a ‘weird’ spot with the bootloader, or you have uploaded code that crashes and doesn’t auto-reboot into the bootloader, double-click the RST button to get back into the bootloader. The red [white] LED will pulse, so you know that its in bootloader mode. Do the reset button double-press right as the Arduino IDE says its attempting to upload the sketch, when you see the Yellow Arrow lit and the Uploading… text in the status bar.

https://learn.adafruit.com/adafruit-feather-32u4-radio-with-lora-radio-module/using-with-arduino-ide