Unknown Downlink after every Uplink

Summary
I am writing an integration for the Wilderness Labs Meadow platform using an SX1272 from Adafruit. This means I am implementing the driver for the SX1272, the driver for LoRa, and a driver for LoRaWAN (I may have bit off more than I can chew…)

I was able to figure out all the OTAA stuff, as device can join my application without a problem. When I send an uplink message , TTN responds immediately with a scheduled downlink message.

I’m not quite sure what to make of it. It is 26 bytes long with an FPort of 0. The contents of the message change with each downlink. I’m not sure if it’s encrypted or not.

Here is the downlink from the TTN Console

    "raw_payload": "YO2W/SeAPwAAyAquB5hKZKPXoIuMxAuo3ckgEVZ74sX/yJlAoZAG",
    "payload": {
      "m_hdr": {
        "m_type": "UNCONFIRMED_DOWN"
      },
      "mic": "QKGQBg==",
      "mac_payload": {
        "f_hdr": {
          "dev_addr": "27FD96ED",
          "f_ctrl": {
            "adr": true
          },
          "f_cnt": 63
        },
        "frm_payload": "yAquB5hKZKPXoIuMxAuo3ckgEVZ74sX/yJk=",
        "full_f_cnt": 63
      }
    },

Hardware
Wilderness Labs Meadow F7
Adafruit RFM95W (Semtech SX1272)

The LoRaWAN specification will tell you downlinks to fport 0 are MAC commands. These instruct your device to adapt settings to match the LNS settings and optimize transmissions. You must implement the MAC commands to be able to use your device with TTN. If you don’t you are generating a DoS attack on the gateway by forcing it to transmit too often. (Sending gateways can’t listen for uplinks and gateways have limited uplink capacity)

Thanks @kersing for the quick response.

If you don’t you are generating a DoS attack on the gateway by forcing it to transmit too often

Luckily the only gateways around seem to be mine (and mine is definitely the one sending the downlink). I definitely don’t want to be spamming other gateways.

Based on my reading of the spec, I figured they were MAC commands, however, I must be doing something wrong because the FRM Payload doesn’t look like any of the MAC commands in the spec

The LoRaWAN specification will tell you downlinks to fport 0 are MAC commands. These instruct your device to adapt settings to match the LNS settings and optimize transmissions.

My reading of the spec says the payload should start with 0x02 through 0x0A with 0x80 to 0xFF being proprietary. Perhaps I’m decrypting the payload incorrectly? I’ve been comparing my implementation to the lora-packet Node.JS library, and I get the same decrypted value as that library. For example, one message, after decrypting the FRM Payload, gives me

E5518A98E71D0FB62D480ABAD9DC7F0C66F298DD33981CE3ED84

Which doesn’t seem to correspond with anything in the LoRa 1.0.2 spec I have. Can you point me to a section in the spec that describes what this message should contain and how to parse it?

An old chestnut - if they aren’t on TTN you won’t be able to see them but they will still have to deal with your uplinks and you are unnecessarily occupying part of the shared ISM spectrum with other users. LoRa is Long Range - your radio waves will impact for miles!

v1.0.2 is old - at the very least implementing v1.0.3 would make your stack airwaves & LNS friendly.

The spec is hugely detailed and very clearly signposted with a table of contents & line numbers - the format of the Join Accept and subsequent MAC commands to alter the CF list & RX1 settings are all there. I’ve read the spec so I know where to look for details, you have to do that a few times before your brain joins up the sections.

Some things to consider:

  • Before building a car, would you learn to drive it first? That is, do you really know LoRaWAN in its normal use?
  • If you popped the hood of a car, do you look at it and think, crumbs, that looks complicated? That is, did you read the entire spec and think, sure, a few weekends work?
  • When thinking about using personal transport, did you review the options and then think, nah, I’ll build my own? That is, have you looked at the other stacks available and have a conscious reason to discard them?
  • When planning your custom hotrod build, did you look at the underlying engineering that’s known to work? That is, have you looked at the source code for the common stacks in use?

I could go on. As chief cheerleader and junior tester of recent work to bring a ‘joins and uplinks’ bare bones code base as close to compliance as feasible, nine months later it’s been released with the expectation of finding wrinkles & corner cases for, well, forever. It was undertaken as it is far more portable than other stacks and has support for the common LoRa radios.

So when I say a solo effort to write such a huge piece of software is crazy, mostly because it’s a duplication of effort but also that testing your own code makes it very hard to see the bugs, please take this as friendly advice.

Given the very niche nature of your endeavour, there will be few volunteers who can answer and they are mostly dealing with the other topics, so response will be slow.

And then there is the translation problem - eventually you will pose a question that is predicated on the use of .NET whereas almost everyone uses C, some C++ with the occasional Python.

Importantly, please stop using TTN as a testing LNS. Setup your own TTI OS in your office - TTN and TTI Sandbox is for trying out LoRaWAN and is used by communities for their deployments - it is not meant to deal with stack development and you will almost certainly be breaching the FUP with your downlinks.

Most importantly, like my colleague, you seem to be hitting the airwaves - Test Driven Development with stubs so you can call functions and verify the outputs was almost designed for this sort of project. And you can feed in downlinks and write the code until the tests pass. And you don’t need to have the entire stack compiled - you can use just the parts you are working on with the common utilities. And you can easily debug the code.

Are you using the right key? Fport 0 FRMPayload is encrypted using the NwkSKey according to paragraph 4.3.3 of the LoRaWAN 1.0.3 specification. (Please use at least that version with the accompanying regional specification if you don’t want to go to 1.0.4 yet.)

Why are you implementing your own version of the stack from scratch? As you probably found by now, implementing the LoRaWAN stack is a major undertaking and there are several ready to go implementations, that have been tested and debugged extensively, around.

Given the very niche nature of your endeavour, there will be few volunteers who can answer and they are mostly dealing with the other topics, so response will be slow.

And then there is the translation problem - eventually you will pose a question that is predicated on the use of .NET whereas almost everyone uses C, some C++ with the occasional Python.

I totally understand. I have spent quite a bit of time working on this before reaching out and don’t expect language specific help here.

Test Driven Development with stubs so you can call functions and verify the outputs was almost designed for this sort of project. And you can feed in downlinks and write the code until the tests pass. And you don’t need to have the entire stack compiled - you can use just the parts you are working on with the common utilities. And you can easily debug the code.

Totally agree. The biggest blocker I have run into in this respect is a lack of suitable “known good” uplink/downlink message bytes I can use for this.

Importantly, please stop using TTN as a testing LNS . Setup your own TTI OS in your office - TTN and TTI Sandbox is for trying out LoRaWAN and is used by communities for their deployments - it is not meant to deal with stack development and you will almost certainly be breaching the FUP with your downlinks.

I setup a private network for this reason. I’ve been using an SDR to verify when data is sent/received, and I’ve been testing the modem at it’s lowest power setting. If you think this insufficient, I can certainly add a faraday cage around end-device/gateway.

Why are you implementing your own version of the stack from scratch?

The Meadow platform runs on C#. As far as I can tell, no such implementation already exists for C#. If you’re aware of one, I’d be happy to use it if the licensing allows.

I appreciate everyone’s feedback so far, I certainly did not undertake this endeavor lightly.

Please use at least that version with the accompanying regional specification if you don’t want to go to 1.0.4 yet.

I’ll go over 1.0.4 and see if I need to make any changes to what I’ve written so far.

Are you using the right key? Fport 0 FRMPayload is encrypted using the NwkSKey

Yeah, I am using the NwkSKey. Under the join message section (6.2.6), it says

Note: An AES decrypt operation in ECB mode encrypts the Join-Accept 1497
frame so that the end-device can use an AES encrypt operation to 1498
decrypt the frame. This way, an end-device has to implement only AES 1499
encrypt but not AES decrypt.

Does this note about how to decrypt the data apply to all messages? I don’t see this noted anywhere else, but it is how I am handling decryption on all encrypted FRMPayloads.

Soooooo very easy to capture from a known good device, gateway & device console. If you put RadioLib in to verbose mode (not SPI mode, that’s just silly) you will get such a wealth of detail that you won’t know what to do with yourself. Although perhaps better to turn on the full debug for LMIC which is v1.0.3 and add more debug as required. It will also help you see how others have mapped out the functions. Get to v1.0.3 first, then you can upgrade to v1.0.4 rather than getting in to a tangle with storing & retrieving sessions.

Quoting bits of the spec will make more sense if you don’t include the line numbers.

And looking at the reference implementation - LoRaMAC-node - and the other common stacks will answer most of this for you, as I can’t stress enough, for most here a stack is a black box. I’d strongly encourage you to ask for help with getting the tools you need so you can do these intricate investigations yourself.

I’ll take a look at getting RadioLib running on one of my boards. Thanks for pointing me in that direction.

Get to v1.0.3 first, then you can upgrade to v1.0.4 rather than getting in to a tangle with storing & retrieving sessions.

That was why I started on 1.0.2, but I’ll focus on 1.0.3 as it seems 1.0.2 is too old.

as I can’t stress enough, for most here a stack is a black box.

Yeah, I know I’m kind of on my own here. I appreciate your help so far.

I’d strongly encourage you to ask for help with getting the tools you need so you can do these intricate investigations yourself.

Are there any that come to mind that you think would be helpful? This is definitely the most complex (and low-level) API I’ve tried to implement, and throwing radio in certainly isn’t making it any easier.

For now, I’m looking to implement the most basic parts of the stack and work up to full support. Is there a pressing reason to support anything beyond Class A for a “minimum viable product”?

Radio is another total dark art of Fresnel zones and antennas and losses in cables etc etc.

But there is no reason to worry about the radio, it’s the same for everyone and there’s no need to run the radio whilst developing - about 2/3rds could be done just on desktop.

I’d very strongly extra strongly recommend getting two or three others from the Wilderness to assist - one for code review and one for testing.

No, that’s enough, B is rarely used, C isn’t used much and it can be added in at a later date - which is why code review is useful so that your coding style is generic enough for others to pick up with any additions or fixes.

But most of the work is on the core + MAC commands and as you’ve seen, you will be obliged to process the settings that the TTI stack sends or you will just keep getting them. This may work for you where you are, but definitely won’t for anyone in an urban environment with other users around them as the gateway will quickly run out of duty cycle.

Don’t code yourself in to a corner with the US channel plan. Or if you do, it will inflict misery on yourself or those that follow when they try to make other regions work - US to AU conversion isn’t a huge leap, EU is quite different. Or delimit the code so that anything very fixed to US is clearly marked so it can be pulled out and reworked.