# LMiC's TX\_COMPLETE event takes 20-30 seconds to fire

**URL:** <https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639>\
**Category:** End Devices (Nodes)\
**Created:** [June 29, 2016, 9:08am UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639 "2016-06-29T09:08:44Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![DiederikRhee](https://www.thethingsnetwork.org/forum/letter_avatar_proxy/v4/letter/d/c37758/32.png) [@DiederikRhee](https://www.thethingsnetwork.org/forum/u/DiederikRhee)\
**Post date:** [June 29, 2016, 9:08am UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/1 "2016-06-29T09:08:44Z")

</div>

A short update of [my project](https://www.thethingsnetwork.org/forum/t/arduino-temperature-node-with-rfm95/2469).

With the help of @matthijs and @platenspeler, my node is now really power effient. I’m using an arduino pro mini 8mhz 3.3V (with led and regulator removed) + RFM95W + DHT22. To join the network I’m using OTAA. When the arduino is in sleep (with lowpower library) it uses around 0.02mA.

The problem now is that it takes really long to send the data. With this code the device wakes up for 20-30 secondes. It looks like the device is waiting for a TX window. Is it possible to remove this? I think it must be possible to send the data really quick.

My code:

> <https://github.com/DiederikRhee/ProMini_Lora_Otaa_temperture/blob/master/Lora_temp_hum/Lora_temp_hum.ino>

---

<div class="post-metadata">

**Author:** ![matthijskooijman](https://www.thethingsnetwork.org/forum/letter_avatar_proxy/v4/letter/m/71c47a/32.png) [@matthijskooijman](https://www.thethingsnetwork.org/forum/u/matthijskooijman)\
**Post date:** [June 29, 2016, 9:25am UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/2 "2016-06-29T09:25:45Z")

</div>

> [@DiederikRhee](#):
>
> It looks like the device is waiting for a TX window. Is it possible to remove this?

No, because then you would violate the duty cycle regulations for the frequencies used.

What you could do, is create messages less often (so you will be certain that the frequencies are “free” again when you do, see [Spreadsheet for LoRa airtime calculation](https://www.thethingsnetwork.org/forum/t/spreadsheet-for-lora-airtime-calculation/1190) for a tool to calculate this), or you could detect the case where your message is waiting for airtime and sleep in the mean time. Doing the latter might be tricky with the current LMIC code, since the next available tx time is not easily exposed. Look at the `engineUpdate` function in `lmic.c` to see what happens when the library decides to wait for airtime.

---

<div class="post-metadata">

**Author:** ![joris](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/joris/32/31402_2.png) [@joris](https://www.thethingsnetwork.org/forum/u/joris)\
**Post date:** [June 29, 2016, 10:42am UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/3 "2016-06-29T10:42:06Z")

</div>

I would suggest to reduce your payload. This is a lot of data:

String message = “T:” + String(t)+“H:” + String(h)+“B:”+ String(b);

Depending on the resolution you need, you can probably reduce this to 2 bytes max!

1 byte for temperature (decide what your expected range will be, for example -20 -\> 50 celcius, then normalize this so you use 1 byte (0…255), For the second byte combine the humidity (0…100) and battery level (0…100). Send those two bytes, and reconstruct your temperature, humidity and battery level in your application (you can even do this in the ttn backend, before the data is delivered by mqtt).

This will drastically reduce the time you have to wait between sending packets.

[edit] on a second thought, you actually need three bytes if you want to have temperature, humidity and battery level. Three bytes is 24 bits. If you use 7 bits for battery, 7 bits for humidity, then you have 10 bits left for temperature. 2^10 = resolution of 1024. For battery level you can even reduce to 5 or 6 bits, which gives you even more resolution for the temperature. Decide what resolutions you need for each as a minimum and fit it in the 24 bits (3 bytes)!

---

<div class="post-metadata">

**Author:** ![BoRRoZ](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/borroz/32/44234_2.png) [@BoRRoZ](https://www.thethingsnetwork.org/forum/u/BoRRoZ)\
**Post date:** [June 29, 2016, 1:45pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/4 "2016-06-29T13:45:25Z")

</div>

battery level you can reduce to 1 bit if you use a SOC module (state of charge)  
you can set an alert level at 30% or 60% or whatever.  
If the battery hits this alert treshold level, the output is one, you can use this as an interrupt and you can read a register

- [https://www.sparkfun.com/products/10617](https://www.sparkfun.com/products/10617)

- [http://blog.codebender.cc/2014/02/13/lifuelgauge/](http://blog.codebender.cc/2014/02/13/lifuelgauge/)

- [https://www.maximintegrated.com/en/app-notes/index.mvp/id/3958](https://www.maximintegrated.com/en/app-notes/index.mvp/id/3958)

- [http://www.lucadentella.it/max17043-libreria-per-arduino/](http://www.lucadentella.it/max17043-libreria-per-arduino/) (tested ok !)

- [https://www.youtube.com/watch?v=3yHRrPDczK4](https://www.youtube.com/watch?v=3yHRrPDczK4)

- [https://www.youtube.com/watch?v=ZtkIDwkrc6E](https://www.youtube.com/watch?v=ZtkIDwkrc6E)

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [June 29, 2016, 5:27pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/5 "2016-06-29T17:27:10Z")

</div>

> [@matthijs](#):
>
> > It looks like the device is waiting for a TX window. Is it possible to remove this?
> 
> No, because then you would violate the duty cycle regulations for the frequencies used.

I _think_ Diederik does not want to transmit more often\*, but just wants to sleep more?

But indeed, it seems that the `EV_TXCOMPLETE` not only takes receive windows into account, but also duty cycles†, and then keeps the device awake. But even then, 20-30 seconds seems _far_ too long for about 21 bytes to send something like `T:12.34H:12.34B:12.34` on SF7, where a 1% duty cycle yields something like 7 seconds waiting time after sending?

Or are the float values much longer than `12.34`, @DiederikRhee?

* * *

\* Even though the current sleep of 64 seconds seems very low for temperature readings, and yields a daily air time [far above the TTN Fair Access Policy](https://www.thethingsnetwork.org/forum/t/limitations-data-rate-packet-size-30-seconds-day-fair-access-policy-nodes-per-gateway/1300) when sending 20+ bytes. So yes, send less often, and send binary, like already suggested.

† While, officially, a _different_ frequency could be used for the next transmission much sooner?

---

<div class="post-metadata">

**Author:** ![Rob65](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/rob65/32/12469_2.png) [@Rob65](https://www.thethingsnetwork.org/forum/u/Rob65)\
**Post date:** [June 29, 2016, 5:37pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/6 "2016-06-29T17:37:34Z")

</div>

It should not take that long to send some data. I have been sending 14 bytes on SF12, that takes just under 2s.

Are you sure the device is not rejoining using OTAA every time it wakes up?

And regarding the interval on which you send data. I was wondering what a usable rate would be. Temperature does not change that much and not that fast. I was thinking of sending 1 message every 15 minutes.

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [June 29, 2016, 6:04pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/7 "2016-06-29T18:04:30Z")

</div>

> [@Rob65](#):
>
> It should not take that long to send some data.

True, but for me `TX_COMPLETE` is also taking about 12-13 seconds for 1 byte on SF7. (LMiC on ESP8266/Arduino.)

That’s:

- 62 ms to actually send data (which indeed I see on the gateway right then), plus
- 2 seconds for `RECEIVE_DELAY2` for the second receive window, plus
- _maybe_, a bit over 5 seconds for the 1% duty cycle on SF7 (which LMiC should only take into account when handing a new transmission, but _it seems_ it might be doing that after sending too), plus
- somehow another 5-6 seconds to make it 12-13 seconds…

I need to peek into the code I guess, though I didn’t mind so far.

---

<div class="post-metadata">

**Author:** ![telkamp](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/telkamp/32/35697_2.png) [@telkamp](https://www.thethingsnetwork.org/forum/u/telkamp)\
**Post date:** [June 29, 2016, 6:49pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/8 "2016-06-29T18:49:04Z")

</div>

It’s not duty cycle related.

LMiC purposely delays the TX\_COMPLETE event to prevent the next transmission to collide with a downlink that the node did not receive, but the gateway is still busy sending (and therefor not in receive mode).

It uses a worst-case for that, at SF12 in the RX2 window, that is defined by DNW2\_SAFETY\_ZONE (in lmic.c) with a default of 3 seconds.

Maybe this is bit conservative, also because TTN is use SF9 in RX2.

We could implement this another way, by not using the TX\_COMPLETE event to prevent too early tx, such that the node can go in sleep as soon as possible after the RX2 preamble window.

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [June 29, 2016, 7:20pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/9 "2016-06-29T19:20:33Z")

</div>

> [@Thomas](#):
>
> LMiC purposely delays the TX\_COMPLETE event to prevent the next transmission to collide with a downlink that the node did not receive

But still, that would only be 3 seconds then (for EU; 750 ms for US I see now), on top of `RECEIVE_DELAY2`. So I still need to peek in the code one day, to see where the other seconds are lost…

(And aha, you explained earlier, but I misinterpreted then.)

---

<div class="post-metadata">

**Author:** ![matthijskooijman](https://www.thethingsnetwork.org/forum/letter_avatar_proxy/v4/letter/m/71c47a/32.png) [@matthijskooijman](https://www.thethingsnetwork.org/forum/u/matthijskooijman)\
**Post date:** [June 29, 2016, 9:14pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/10 "2016-06-29T21:14:28Z")

</div>

What I meant was that, if you tell LMIC to transmit a packet shortly after a previous packet (e.g. when the duty cycle limitation from that previous packet is still preventing TX), then that duty cycle limitation will indeed delay TX\_COMPLETE (but not from the duty cycle delay _after_ the next packet, but _before_ the next packet). This is not something that I think should change.

What you’d want to do in this case is sleep until the TX time has approached. LMIC does not currently offer a solid API to get at this info. Until this is changed, you could:

- Detect that TX is delayed by checking `LMIC.opmode` for the `OP_TXRXPEND` (or something similar, didn’t check just now) bit. If it is set, TX is happening, if it is unset (after queueing a packet), LMIC is waiting for airtime to become available. In the latter case, you could cancel your packet, sleep for some time and try again, until you eventually see the TXRXPEND bit set. I’m using this approach in a sketch I’m using.
- In addition to the above, you could pry in LMIC’s list of “osjobs” to see when the next job is pending, and sleep that long. In either case, you’ll have to make sure that the `micros()` counter is updated through sleep, which does not normally happen on AVR.

Thinking of the latter, the fact that `micros()` does not update might even be the cause of your problem in the first place: you queue a new packet after the duty cycle delay is over, but because you’ve been sleeping, LMIC doesn’t _know_ that time has passed. Ideally, the `micros()` counter would be adapted to compensate for the sleep time which requires some changing some global variable internal to the Arduino code, but that might not be so portable. I wonder if Arduino should get a API to do this instead…

> [@Thomas](#):
>
> We could implement this another way, by not using the TX\_COMPLETE event to prevent too early tx, such that the node can go in sleep as soon as possible after the RX2 preamble window.

I’m planning to change this in the LMIC library, see [this comment](https://github.com/matthijskooijman/arduino-lmic/issues/16#issuecomment-226441917) about what I’m planning to change if you want to do this now (to save these 3 seconds of “safety zone”).

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [November 5, 2017, 4:36pm UTC](https://www.thethingsnetwork.org/forum/t/lmics-tx-complete-event-takes-20-30-seconds-to-fire/2639/11 "2017-11-05T16:36:27Z")

</div>

> [@telkamp](#):
>
> LMiC purposely delays the TX\_COMPLETE event to prevent the next transmission to collide with a downlink that the node did not receive, but the gateway is still busy sending (and therefor not in receive mode).
> 
> It uses a worst-case for that, at SF12 in the RX2 window, that is defined by DNW2\_SAFETY\_ZONE (in lmic.c) with a default of 3 seconds.

Only now I noticed that since February @matthijs’ LMiC port the event itself is no longer delayed. Nice!

> <https://github.com/matthijskooijman/arduino-lmic/commit/6e098863>
>
> Before, when no packet was received in RX2, there would be a 3-5 second
> delay (f…or EU868) before calling \`processDnData()\` and emitting
> EV\_TXCOMPLETE. This was the \`DNW2\_SAFETY\_ZONE\` to prevent a subsequent
> TX to collide with a downlink packet that was ongoing but not locked on
> to.
> 
> With this change, \`txDelay()\` is used to still delay the subsequent TX,
> without delaying the \`EV\_TXCOMPLETE\` event, which allows battery-powered
> devices that wait for this event before sleeping to get an extra 3-5
> seconds of sleep for each transmitted packet, improving battery life.
> 
> Closes: #16
