LMIC get duty cycle timer for channel

Hi guys :slight_smile:

First of all I’m new in the TTN community and Im surprised how many people are sharing Information here. Wow!

We build a single Channel gateway for development and an Atmega328p based node with the LMIC library.

Now the problem: After successful OTAA I call LMIC_disableChannel(i); for each channel except channel 0 to force the Node to use our single channel gateway frequency.

When I try to send a confirmed Downlink message, the message is received by the node but no ACK message is received by the Gateway/TTN. At another place with a multichannel gateway everything works as expected.

So my thoughts were that the ACK package in single channel mode isn’t sent because of the duty cycle . If I add delay(60*1000); in the event handler method right after the downlink message is parsed the ACK packet is sent after a minute without a problem (100% reproducible).

I don’t want to hardcode the minute delay because the duty cycle time depends on the Payload and SF.

So my question: Is there any option to get the next possible TX window time for a channel from the LMIC library? If not maybe someone already wrote a method to calculate the delay between the windows?

Who is sending the ACK? Your own code, or the LMIC library? Please show us your changes.

If your own code is sending it, then for regular uplinks I’ve always seen LMIC postpone the transmission until a channel became available, without any additional hacks. So I guess it’s in the LMIC code?

As an aside, maybe the application shouldn’t care how soon your device acknowledges. You could even wait for the next uplink, emphasis mine:

4.3.1.2 Message acknowledge bit and acknowledgement procedure (ACK in FCtrl)

When receiving a confirmed data message, the receiver shall respond with a data frame that has the acknowledgment bit (ACK) set. If the sender is an end-device, the network will send the acknowledgement using one of the receive windows opened by the end-device after the send operation. If the sender is a gateway, the end-device transmits an acknowledgment at its own discretion.

Note: To allow the end-devices to be as simple as possible and have as few states as possible it may transmit an explicit (possibly empty) acknowledgement data message immediately after the reception of a data message requiring a confirmation. Alternatively the end-device may defer the transmission of an acknowledgement to piggyback it with its next data message.

And making all other code stop by using delay might not be a great idea either? Finally, as you’re new to TTN, be sure to read Fair Use Policy explained. Welcome!

1 Like

The ACK is sent by the library. LMIC send a message with empty payload. If the Node is able to use multiple channels the ACK is sent right after the downlink is received. If I use the single channel mode, the Library tells me that something is sent (Must be the ACK) but this isn’t the case in fact. The gateway receives nothing. It can’t be the case because an immediately ACK after receiving a downlink message would violate the duty cycle.

Alternatively the end-device may defer the transmission of an acknowledgement to piggyback it with its next data message.

That would be really nice. It saves airtime and the result is the same. How can I archive this in LMIC?

This my current code:

It first sends the ACK (its automatically scheduled) and then the next frame after TX_INTERVAL seconds

case EV_TXCOMPLETE:
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      if (LMIC.txrxFlags & TXRX_ACK)
        Serial.println(F("Received ack"));
      if (LMIC.dataLen) {
        Serial.print(F("Received "));
        Serial.print(LMIC.dataLen);
        Serial.println(F(" bytes of payload"));
        byte received[LMIC.dataLen];
        for (int i = 0; i < LMIC.dataLen; i++) {
          if (LMIC.frame[LMIC.dataBeg + i] < 0x10) {
            Serial.print(F("0"));
          }
          received[i] = LMIC.frame[LMIC.dataBeg + i];
          Serial.print(received[i], HEX);
        }
        Serial.println();
        handleCommand(received, LMIC.dataLen);
#ifdef SINGLE_CHANNEL
        //Duty cycle ACK
        delay(60000);
#endif
      }
// Schedule next transmission
      os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send);
      break;

Ah, as the delay is in your own code, I wonder what would happen if you schedule an (empty) uplink instead, in the same location in the code?

Maybe this should work but then a Frame without payload would be sent even if no ACK was requested.

Alternatively the end-device may defer the transmission of an acknowledgement to piggyback it with its next data message.

I think this is the best solution even for the production use with all channels enabled. Thank you for the tip! I tested it with the SF12 mode to see if the library calculates the duty cycle correct (See the code below which works perfectly for me). If I schedule a data message without delay the LMIC library waits as expected for the next free TX window even my TX_INTERVAL is to short.

case EV_TXCOMPLETE:
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      if (LMIC.txrxFlags & TXRX_ACK)
        Serial.println(F("Received ack"));
      if (LMIC.dataLen) {
        Serial.print(F("Received "));
        Serial.print(LMIC.dataLen);
        Serial.println(F(" bytes of payload"));
        byte received[LMIC.dataLen];
        for (int i = 0; i < LMIC.dataLen; i++) {
          if (LMIC.frame[LMIC.dataBeg + i] < 0x10) {
            Serial.print(F("0"));
          }
          received[i] = LMIC.frame[LMIC.dataBeg + i];
          Serial.print(received[i], HEX);
        }
        Serial.println();
        handleCommand(received, LMIC.dataLen);
        // Schedule next transmission
        //os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send);

      }
      //Just for debugging to finish the Serial.println(xy) before going to sleep
      delay(100);
      for (int i = 0; i < int(TX_INTERVAL / 8); i++) {
        // Use library from https://github.com/rocketscream/Low-Power
        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
      }
      do_send(&sendjob);
      break;

Of course its possible to replace the LowPower with a simple delay(TX_Interval);

Yes of course! This limitations are no problem for us. We plan with one transmission every 30 minutes on SF 7 and maybe two downlinks per Day to adjust settings and later when everything works one downlink per week. I hope it is ok too exceed this limitations during development. My lab is in a cellar so I should disturb no one.

Another question wich is off topic but I don’t want to annoy the community with another topic because its a simple one :smiley: : The RAK831 supports 8 channels. So is it able to use the 9th TTN RX2 channel (switching one channel temporarily for downlink)?

Determining if an ACK was needed would be your next problem. :wink: Peeking into the LMIC code suggests that LMIC.OP_POLL might tell you if an ACK is pending.

OP_POLL     = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data

But if sleeping until the next uplink is due does not give you the additional automatic ACK, then that’s even better of course. Are you saying that the code you posted last indeed achieves that, so no empty ACK is transmitted?

I don’t know if sleeping in the event handler causes any problem; it feels like it’s preventing LMIC from doing proper housekeeping. As for sleeping, beware for rollover of the clock: Lmic plus rfm95w staying awake for long periods - #9 by tomtor.

See also Serial.flush().

I’d ask that in The hard RAK831 cafe part 3. Also, please see How do I format my forum post? [HowTo]

1 Like

The defined channels are for reception. For transmission the backend can specify any frequency in the band used, in fact it will specify the frequency for each downlink even if a defined channel frequency is used.

1 Like

Good hint! Thanks!

Yes!
As you can see in the image the ACK is received and the uplink contains payload.

Nice
It seems like I have to buy one…

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.