TTN Node - link check does not work?

This should be supported in V2 too, and should work in the Arduino library since July 2017, given the example log (and its explanation) in the pull request that added the feature.

If you’re really not seeing the request going out, then I’d say it doesn’t even matter which backend you’re using. But why are you expecting an empty payload? The linkCheck command is delegated to the RN2483 module by using mac set linkchk, which is documented as:

When the time interval expires, the next application packet that will be sent to the server will include also a link check MAC command.

So, I feel it will not be sending standalone packets just for the link check. So, are you sure one of the regular uplinks does not include the MAC command?

If you actually do see the MAC command: are you sure you can receive downlinks? (Like: are you using OTAA?) And anything in the gateway’s Traffic page in TTN Console?

Thank you for your quick response!

First of all, the zero-payload of the uplink was nonsense of course. I referred to the LoRaWANv1.1 spec where it reads:

With the LinkCheckReq command, an end-device may validate its connectivity with the
network. The command has no payload.

The command has no payload, but not the whole message. I should read more carefully.

So, are you sure one of the regular uplinks does not include the MAC command?

No, now I am actually not sure.

are you sure you can receive downlinks? (Like: are you using OTAA?)

I use OTAA (with Brocaar right now). I get uplinks, but no downlinks. Do I need to schedule downlinks by myself with an application or will the network server respond by itself with a downlink just containing the LinkCheckAns command?
Now, I am using TTN again and I also scheduled some downlinks (with nonsense-payload) to carry the LinkCheckAns piggy-back, but the following uplinks do not contain valid values for demod_margin and num_gateways.

The LoRaWANv1.1 spec just says:

When a LinkCheckReq is received by the Network Server via one or multiple gateways, it
responds with a LinkCheckAns command.

???

If OTAA works, then you do get downlinks, as the OTAA Join Accept is a downlink too. So, you’re sure downlinks work. Also, for LinkCheckAns you do not need to schedule a downlink yourself, I think.

To make sure your uplinks include the LinkCheckReq command, paste a raw packet from the gateway into an online decoder and then show us the values for FCtrl and FOpts (or show us a full packet here). While testing, make sure to use a very small value (I’d say: 1 second) in ttn.linkCheck, to increase the chances that a LinkCheckReq is requested.

Also, I’ve no idea if the Brocaar version supports it. TTN should.

You are aware TTN has not implemented v1.1? (Yet)

1 Like

For TTN I get something like this:

Assuming hex-encoded packet
1240001C09E3FF000CDE

Message Type = Join Request
  PHYPayload = 1240001C09E3FF000CDE

( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )
        MHDR = 12
  MACPayload = 40001C09E3
         MIC = FF000CDE (from packet) INVALID (tried MSB 0000-FFFF)
             = AF291AA5 (expected, assuming 32 bits frame counter with MSB 0000)

( MACPayload = AppEUI[8] | DevEUI[8] | DevNonce[2] )
      AppEUI = 0C00FFE3091C0040
      DevEUI = DE
    DevNonce = 

With Brocaar, I get the following
uplinks brocaar.txt (5.8 KB)

FOpts is always null. :frowning:

You are aware TTN has not implemented v1.1? (Yet)

No, I wasn’t. Nevertheless, the link check should still work, I guess. Brocaar will negotiate the max. supported minor version of LoRaWAN with the TTN node during the OTAA anyway.

You’re showing the Join Request. Instead, you’ll want to look at the uplinks after the device has joined.

I did look at a normal uplink. I don’t know why it tells me it is a join request.

Another trial way after successful join:
Screenshot%20from%202018-11-23%2013-03-56

Assuming hex-encoded packet
1240002509B8FF000CDE

Message Type = Join Request
  PHYPayload = 1240002509B8FF000CDE

( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )
        MHDR = 12
  MACPayload = 40002509B8
         MIC = FF000CDE (from packet) INVALID (tried MSB 0000-FFFF)
             = 3DEF4FEF (expected, assuming 32 bits frame counter with MSB 0000)

( MACPayload = AppEUI[8] | DevEUI[8] | DevNonce[2] )
      AppEUI = 0C00FFB809250040
      DevEUI = DE
    DevNonce =

Ah, you’re pasting the application payload into that decoder. You should use the full LoRaWAN packet from the gateway. (Like from the gateway’s Traffic page in TTN Console.)

:man_facepalming:
actual_packets_decoded.txt (2.8 KB)

There you go. Recorded with TTN. Still, no FOpts.

Indeed, so no LinkCheckReq. :frowning:

Did you minimize the time in ttn.linkCheck(1)?

(Note that you’re missing some values of the frame counter; jumping from 37, 39 to 47, but maybe you received all?)

 uint16_t interval = 1;
  ttn.linkCheck(interval);

He had his chance. :grin:

I did some random selection of frames. I received all frames of course.

isn’t a linkcheck a kind of downlink message /ack and will there be fair use limitations ? @htdvisser

I don’t know about any.

Nothing changed since 2016 (Questions regarding the TTN fair access policy - #4 by htdvisser):

Everything counts. Each uplink counts towards the total uplink, and each downlink (including ACKs) counts towards the total downlink.

1 Like

Like Hylke confirmed it is subject to the Fair Access Policy. But as that is not enforced yet, your tests should not be affected by that. (Also, so far I think the problem is that the device is not even asking for the link check.)

Ah, finally unpacked my The Things Node to test it myself :wink:

It seems you need to call ttn.linkCheck(interval) after you’ve joined. So, this works for me:

ttn.join(appEui, appKey);

// Set the time interval in seconds for the link check process to be triggered;
// 0 will disable the link check process. When the time interval expires, the
// next application packet that will be sent to the server will include also a
// link check MAC command in the LoRaWAN FOpts field.
uint16_t interval = 1;
ttn.linkCheck(interval); 

Of course, interval = 1 is just for testing.

This yields, e.g., 40DA22012601080002020A3B48DAB7EFC8B9A7461D249E being:

      ( FHDR = DevAddr[4] | FCtrl[1] | FCnt[2] | FOpts[0..15] )
     DevAddr = 260122DA (Big Endian)
       FCtrl = 01
        FCnt = 0008 (Big Endian)
       FOpts = 02

Here, FCtrl indicates that there’s 1 byte in FOpts:

And that one byte is a LinkCheckReq:

The response is, e.g., 60DA2201260307000210016C6E99E6, or:

      ( FHDR = DevAddr[4] | FCtrl[1] | FCnt[2] | FOpts[0..15] )
     DevAddr = 260122DA (Big Endian)
       FCtrl = 03
        FCnt = 0007 (Big Endian)
       FOpts = 021001

Here, the 0x02 in 021001 decodes to LinkCheckAns, along with:

So, 0x10 = margin = 16 dB, and 0x01 = gateway count = 1.

Enjoy :slight_smile:

3 Likes

That is correct. The LinkCheckAnswer will come directly in the network reply in RX1 or RX2.

Ahaaaa … Thank you all very much for your great support, and in particular I want to thank you Arjan, for providing the solution!
Now it also works for me. :smiley:
uplink_with_received_%20linkcheck_data

Could someone add a hint to the API documentation, because this does not seem that obvious to me?

The documentation has already been updated a bit.

Thanks so much for providing these traces. I’ve written my own network server and didn’t understand the conditions under which an UnconfirmedDataDown message could be sent. I was handling LinkCheckReq only for ConfirmedDataUp!