Tiny Node adoption for The Things Stack V3

I was quite surprised and and a little bit shocked by this message on the Things Stack (V2) Console:

What does it mean?, how do I get my running LoRa nodes to the V3 Stack? How much work must be done? Can I still use all my nodes?

My nodes still working on V3?

In the past years I developed two main nodes: One based on a RFM95W module and ATtiny84 controller (8k flash) and one based on a RFM95W module and a STM32L051 controller (64k flash) called the MiniPill LoRa.

I was not afraid this last one could handle the Things Stack V3. I have enough memory to run OTAA and latest LMIC library, although some use the proprietary library to get more flash for the sensor-code they use.

The main problem was the ATtiny84 with proprietary library for the LoRaWAN communication. It can handle only ABP and cannot receive downlink messages. In most situations you do not need them. You only want to have upload the data from your sensor periodically and use as minimal power as possible.

The testing phase

I have build a V3 gateway and got it up and running (see previous blog) and hence had a test environment for my nodes. I have a few MiniPill LoRa nodes with the proprietary library for testing and of course the LMIC1.5 (used) and the new LMIC1.6 library. Most code is Arduino based, and sometimes proprietary for the STM32 or ATtiny84 controller. The first test show a good result.

Problem description

The main problems of ABP nodes that cannot receive and react on downlink messages is that the Stings Stack V3 keeps on sending downlink messages until the node responds. The Things Stack assumes every node will comply to the the LoRaWan specification. There are two situations the Things Stack will send messages to the node:

  • After the first received packet the send the MAC command RXParamSetupReq (0x08), this one must be answered with a RXParamSetupAns. On the Stack V3 the policy is to set the RX1 delay to 5 seconds instead of the default 1 second.
  • After a period of time or frame count the Things Stack will send the MAC command DevStatusReq (0x06), this one must be answered with a DevStatusAns. I have figured out that these messages are usualy send at 201, 402, 603, etc. It seems to me the calclation is 200 + downlink frame counter.

When you do not answer these request the downlink messages will continue. This is not a nice situation. With several nodes you get a lot of messages send by your gateway in the air. I do wonder why there is not a maximum of downlink request set on this, but that is another discussion. I also wonder why they send DevStatusReq packets by default, me and my node do not want to give this information to the Stack.

Update 2022-08-14 schedule-downlinks = false

As the solution below worked for 2^16 (65536) uplinks, after the turnover of the framecounter, for most nodes the downlink started again. Fortunately TTN have added a feature, see this item on the forum:


This helped stopping the downlinks on my nodes, and gave me time to update my nodes and firmware.

Warning before using this solution

Use this solution and code examples on your own risk. I do encourage you to use a node with the latest LMIC library that is compliant with the LoRaWAN specification. However I really can imagine that the time, money and effort you put in your existing nodes you do not want to throw away. I hope to help you with this article to move your existing nodes to the Things Stack V3. The Things network is build upon the efforts, money and time spend by many people with technical interest and I hope this will continue with support for legacy (V2) nodes.
Be aware that some people on the The Things Stack will be upset by this hacked solution. When you can please use the LMIC code for the MiniPill LoRa Node, see links to that code on other blog-articles.

Update 2022-08-14 schedule-downlink


The solution is basically based on two things:

  1. Answer the RXParamSetupReq (0x08) after sending the first frame as if you could receive the downlink package.
  2. Disable the DevStatusReq (0x06) for the node that cannot or you do not want to receive Device Status Requests.

The first point was easy to accomplish. Just send a well formed RXParamSetupReq send on the second frame. This means a little addition of code to the proprietary library.
The Things stack fortunately gave a hint to the solution for the second point:

To set the StatusTimePeriodicity and StatusCountPeriodicity to 0 you have to use the CLI (Command Line Interface) from the Things Stack V3.

In the next paragraphs I will describe how to create a new ABP node on the The Things Stack V3 and make the modification so you can use it as it was connected to the Things Stack V2.

First Step: create a new node on the things Stack

Updated 2022-01-06: due to new version of The Things Stack (3.16.2) and hence new possibilities on the interface. These settings are not tested with new devices yet.

Create an Application and then add a new node on The Things Stack V3

ABP settings on The Things Stack V3

Due to updates on the interface on The Things Stack, you have to use some advanced options to be able to change or add these settings.

  • Add a new End Device in V3 (Manual)
  • Frequency Plan:
    For EU868 devices coming from V2, you select Europe 863-870 MHz (SF9 for RX2 – recommended)
  • LoRaWAN version:
    is probably v1.0.2
  • Regional Parameter version:
    is probably PHY v1.0.2 rev B

    select first the line Show advanced activation, LoRaWAN and Cluster settings before you can do next step:
  • Select Activation by Personalization (ABP)
  • Aditional LoRaWAN class capabilities:
    None (Class A only)
  • Leave the selected tick box Use network’ s default MAC settings
  • Set the ID and security parameters:
    The DevEUI is optional, set all other security and id settings
  • Register the end Device.

  • in Advances settings options at Network layer settings:
    (can be set later or checked)
    • The Frame counter width is probably 16 bit
    • The RX1 Delay is 5 seconds
    • The RX1 Data Rate Offset is 0
    • Optional Resets Frame Counters Enabled
      this options disable dropping messages after reseting the frame counter. This was not working until Things Stack v3.12.
    • The RX2 Data Rate Index for EU868 devices is 3
    • The RX2 Frequency for EU868 devices is 869525000
      This setting is optional:
    • The Factory Preset Frequencies for EU868 devices with 8 channels is:
      • 868100000, 868300000, 868500000
      • 867100000, 867300000, 867500000, 867700000, 867900000
    • Status count periodicity is set default to 200, change this to 0. after refreshing this page it is stil on 200. Strange behavior. This parameter is also set by the cli commands below.
    • Status time periodicity is set default to 0, no change needed
      This parameter is also set by the cli commands below.
    • Use ADR, default ticked, disable this.

This setup can also be done with the CLI bus is not described here.

Second Step: set StatusTimePeriodicity and StatusCountPeriodicity to 0

Use the ttn-lw-cli and login to the Things Stack. Read the paragraph at the end of this blog for information how to connect with the CLI to the Community version of the Stack V3.

Get a list of applications

ttn-lw-cli applications list

Get a list of devices for application

ttn-lw-cli end-devices list <your application-id>

Set and get status-count-periodicity to 0

ttn-lw-cli end-devices set <your application-id> <your device-id> --mac-settings.status-count-periodicity 0

ttn-lw-cli end-devices get <your application-id> <your device-id> --mac-settings.status-count-periodicity

Set and get status-time-periodicity to 0 seconds

ttn-lw-cli end-devices set <your application-id> <your device-id> --mac-settings.status-time-periodicity 0

ttn-lw-cli end-devices get <your application-id> <your device-id> --mac-settings.status-time-periodicity

Get and reset frame counters

Sometimes you have to reset the counters after a reboot of your device or when you are testing. Otherwise the frames will be rejected due to difference of counters.

ttn-lw-cli end-devices get <your application-id> <your device-id> --session.last-f-cnt-up

ttn-lw-cli end-devices reset <your application-id> <your device-id> --session.last-f-cnt-up

ttn-lw-cli end-devices get <your application-id> <your device-id> --session.last-a-f-cnt-down

ttn-lw-cli end-devices reset <your application-id> <your device-id> --session.last-a-f-cnt-down

Third Step: Use the code with the proprietary library

Use the code with the proprietary library and use the #define TTNSTACKV3 setting at the start of the main.cpp file. In that case the second frame send after reboot will send a confirming RXParamSetupAns. See the code in LoRaWAN.cpp file.

Links to updated code with proprietary libraries for the ATtiny node and MiniPill LoRa Node:



The code is not yet extensive tested. First test are running and looking good.

CLI on Community The Things Stack V3

To get the CLI working you should take a few steps:

  1. Install the ttn-lw-cli on your operating system. This is described here: https://www.thethingsindustries.com/docs/getting-started/cli/installing-cli/
  2. Create a Personal API key. This is the an easy way to setup a trust between your PC and the Stack:
    1. Login to the Console of the Things Stack V3
    2. Click on the right top corner on your login name
    3. Select Personal API keys
    4. Click on Add API Key
    5. Add a key and copy the one-time-showing generated key. This is the key (quite long) you should use in one of the next steps
  3. run the command to setup the configuration: ttn-lw-cli use eu1.cloud.thethings.network
    In the documentation of The Things Stack V3 the server name <tenant-id>.eu1.cloud.thethings.industries is used.
  4. login to the Stack: ttn-lw-cli login --api-key <key derived from previous step>
  5. Now you can run the commands to set StatusTimePeriodicity etc.

Technical background on RXParamSetupAns

The Things stack will send and frame with the next FOpts MAC commands:

05 03 d2 ad 84 06

05 = RXParamSetupReq

03 d2 ad 84 = Parameters of the RXParamSetupReq with RX1 timing and channels used

06 = DevStatusReq

In the code I only respond to the 05 RXParamSetupReq with a 05 (RXParamSetupAns) command and 07 parameter (all parameters successful set). I do ignore the 06 DevStatusReq. In my experiments the downlink messages stopped after answering with RXParamSetupAns.


I discovered that after a reset of the downlink counter (by accident) the RXParamSetupReq is resent without the DevStatusReq. The Things Stack will keep resending this message until it is answered. Be careful on resetting counters with the CLI!

9 thoughts on “Tiny Node adoption for The Things Stack V3

  1. Pingback: ATtiny84 Low power LoRa node | iot-lab.org

  2. Leo Post author

    Thanks Uli, and for the link.
    Please mind the downlink frames. I have already transferred 5 ABP devices with 10.000 uplinks and no downlinks.
    Indeed, many enthousiast users will have to migrate/adapt while a their nodes are working just fine with V2.

    Kind regard, Leo.

  3. JFP

    Thanks for excellent guidance.

    Setting up an ESP8266 with RFM95 as node with ABP was plain sailing, thanks to your detailed tips.

  4. Marco

    Yes, I was also surprised; your system is running fine and then you can tear it down and rebuild it.
    Fortunately, there are people like you who have knowledge and are willing to look for solutions and also share them.
    I now have a number of your devices operational in V3 …thank Leo for your contribution!


  5. Leo Post author

    Thanks Marco, alway nice to help others!


  6. Leo Post author

    Glad your setup is working with my info!


  7. Thomas

    Hi, I got stucked with the ttn-lw-cli tool. I get the following error:
    $ ttn-lw-cli applications list
    error:pkg/tenant/middleware:missing_tenant_id (missing tenant ID)
    I don’t know what this tenant ID is and how to put it in my config file.

  8. Leo Post author

    I am very sorry, my answer is so late, I do not look very often at the backend, and usually the posts are forwarded to my mail. But is seems that it failed.
    I hope that you found an answer in the meantime.

    Ah, this is a common problem with TTN.

    The tenant ID for community is:


    Remember that you use .network!

    My .ttn-lw-cli.yml file looks like this: (it’s in your home directory)

    config: []
    format: console
    level: info
    credentials-id: eu1.cloud.thethings.network
    input-format: json
    output-format: json
    allow-unknown-hosts: false
    oauth-server-address: https://eu1.cloud.thethings.network/oauth
    identity-server-grpc-address: eu1.cloud.thethings.network:8884
    gateway-server-enabled: true
    gateway-server-grpc-address: eu1.cloud.thethings.network:8884
    network-server-enabled: true
    network-server-grpc-address: eu1.cloud.thethings.network:8884
    application-server-enabled: true
    application-server-grpc-address: eu1.cloud.thethings.network:8884
    join-server-enabled: true
    join-server-grpc-address: eu1.cloud.thethings.network:8884
    device-template-converter-grpc-address: eu1.cloud.thethings.network:8884
    device-claiming-server-grpc-address: eu1.cloud.thethings.network:8884
    qr-code-generator-grpc-address: eu1.cloud.thethings.network:8884
    packet-broker-agent-grpc-address: eu1.cloud.thethings.network:8884
    insecure: false
    ca: “”
    dump-requests: false
    skip-version-check: false

    Yours should look like the same.

Leave a Reply

Your email address will not be published. Required fields are marked *